home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 43
/
Aminet 43 (2001)(GTI - Schatztruhe)[!][Jun 2001].iso
/
Aminet
/
comm
/
tcp
/
smbfs.lha
/
source
/
main.c
< prev
next >
Wrap
C/C++ Source or Header
|
2001-04-07
|
116KB
|
5,629 lines
/*
* $Id: main.c,v 1.64 2001/04/07 11:32:29 olsen Exp $
*
* :ts=4
*
* SMB file system wrapper for AmigaOS, using the AmiTCP V3 API
*
* Copyright (C) 2000-2001 by Olaf `Olsen' Barthel <olsen@sourcery.han.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* smbfs arbeitsgruppe pcguest debug=2 //sourcery/all
*/
#include "system_headers.h"
#include "assert.h"
/****************************************************************************/
#include "smb_abstraction.h"
/****************************************************************************/
#include "smbfs_rev.h"
STRPTR Version = VERSTAG;
/****************************************************************************/
#define SAME (0)
#define OK (0)
#define NOT !
#define ZERO ((BPTR)NULL)
/****************************************************************************/
#ifdef __SASC
#define FAR __far
#define ASM __asm
#define REG(x) register __ ## x
#define INLINE __inline
#else
#define FAR
#define ASM
#define REG(x)
#define INLINE
#endif /* __SASC */
/****************************************************************************/
#define UNIX_TIME_OFFSET 252460800
#define MAX_FILENAME_LEN 256
/****************************************************************************/
#define SMB_ROOT_DIR_NAME "\\"
#define SMB_PATH_SEPARATOR '\\'
/****************************************************************************/
typedef STRPTR KEY;
typedef LONG * NUMBER;
typedef LONG SWITCH;
/****************************************************************************/
struct FileNode
{
struct MinNode fn_MinNode;
struct FileHandle * fn_Handle;
LONG fn_Offset;
LONG fn_Mode;
smba_file_t * fn_File;
STRPTR fn_FullName;
};
struct LockNode
{
struct MinNode ln_MinNode;
struct FileLock ln_FileLock;
smba_file_t * ln_File;
BOOL ln_RestartExamine;
UWORD ln_Pad;
STRPTR ln_FullName;
};
/****************************************************************************/
/* These are in amiga.lib */
APTR ASM AsmCreatePool(REG(d0) ULONG memFlags,REG(d1) ULONG puddleSize,REG(d2) ULONG threshSize,REG(a6) struct Library * SysBase);
VOID ASM AsmDeletePool(REG(a0) APTR poolHeader,REG(a6) struct Library * SysBase);
APTR ASM AsmAllocPooled(REG(a0) APTR poolHeader,REG(d0) ULONG memSize,REG(a6) struct Library * SysBase);
VOID ASM AsmFreePooled(REG(a0) APTR poolHeader,REG(a1) APTR memory,REG(d0) ULONG memSize,REG(a6) struct Library * SysBase);
/****************************************************************************/
/* Forward declarations for local routines. */
STATIC VOID DisplayErrorList(VOID);
STATIC VOID AddError(STRPTR fmt, va_list args);
STATIC LONG CVSPrintf(STRPTR format_string, va_list args);
STATIC VOID VSPrintf(STRPTR buffer, STRPTR formatString, va_list args);
STATIC VOID SendDiskChange(ULONG class);
STATIC struct FileNode *FindFileNode(STRPTR name, struct FileNode *skip);
STATIC struct LockNode *FindLockNode(STRPTR name, struct LockNode *skip);
STATIC LONG CheckAccessModeCollision(STRPTR name, LONG mode);
STATIC LONG NameAlreadyInUse(STRPTR name);
STATIC BOOL IsReservedName(STRPTR name);
STATIC LONG MapErrnoToIoErr(int error);
STATIC VOID INLINE TranslateBName(UBYTE *name, UBYTE *map);
STATIC VOID INLINE TranslateCName(UBYTE *name, UBYTE *map);
STATIC BOOL ReallyRemoveDosEntry(struct DosList *entry);
STATIC VOID Cleanup(VOID);
STATIC BOOL Setup(STRPTR program_name, STRPTR service, STRPTR workgroup, STRPTR username, STRPTR opt_password, BOOL opt_changecase, STRPTR opt_clientname, STRPTR opt_servername, int opt_cachesize, STRPTR device_name, STRPTR volume_name, STRPTR translation_file);
STATIC VOID INLINE ConvertBString(LONG max_len, STRPTR cstring, APTR bstring);
STATIC VOID INLINE ConvertCString(LONG max_len, APTR bstring, STRPTR cstring);
STATIC LONG BuildFullName(STRPTR parent_name, STRPTR name, STRPTR *result_ptr);
STATIC BPTR Action_Parent(struct FileLock *parent, LONG *error_ptr);
STATIC LONG Action_DeleteObject(struct FileLock *parent, APTR bcpl_name, LONG *error_ptr);
STATIC BPTR Action_CreateDir(struct FileLock *parent, APTR bcpl_name, LONG *error_ptr);
STATIC BPTR Action_LocateObject(struct FileLock *parent, APTR bcpl_name, LONG mode, LONG *error_ptr);
STATIC BPTR Action_CopyDir(struct FileLock *lock, LONG *error_ptr);
STATIC LONG Action_FreeLock(struct FileLock *lock, LONG *error_ptr);
STATIC LONG Action_SameLock(struct FileLock *lock1, struct FileLock *lock2, LONG *error_ptr);
STATIC LONG Action_SetProtect(struct FileLock *parent, APTR bcpl_name, LONG mask, LONG *error_ptr);
STATIC LONG Action_RenameObject(struct FileLock *source_lock, APTR source_bcpl_name, struct FileLock *destination_lock, APTR destination_bcpl_name, LONG *error_ptr);
STATIC LONG Action_DiskInfo(struct InfoData *id, LONG *error_ptr);
STATIC LONG Action_Info(struct FileLock *lock, struct InfoData *id, LONG *error_ptr);
STATIC LONG Action_ExamineObject(struct FileLock *lock, struct FileInfoBlock *fib, LONG *error_ptr);
STATIC BOOL NameIsAcceptable(STRPTR name, LONG max_len);
STATIC LONG Action_ExamineNext(struct FileLock *lock, struct FileInfoBlock *fib, LONG *error_ptr);
STATIC LONG Action_ExamineAll(struct FileLock *lock, struct ExAllData *ed, ULONG size, ULONG type, struct ExAllControl *eac, LONG *error_ptr);
STATIC LONG Action_Find(LONG action, struct FileHandle *fh, struct FileLock *parent, APTR bcpl_name, LONG *error_ptr);
STATIC LONG Action_Read(struct FileNode *fn, APTR mem, LONG length, LONG *error_ptr);
STATIC LONG Action_Write(struct FileNode *fn, APTR mem, LONG length, LONG *error_ptr);
STATIC LONG Action_End(struct FileNode *fn, LONG *error_ptr);
STATIC LONG Action_Seek(struct FileNode *fn, LONG position, LONG mode, LONG *error_ptr);
STATIC LONG Action_SetFileSize(struct FileNode *fn, LONG position, LONG mode, LONG *error_ptr);
STATIC LONG Action_SetDate(struct FileLock *parent, APTR bcpl_name, struct DateStamp *ds, LONG *error_ptr);
STATIC LONG Action_ExamineFH(struct FileNode *fn, struct FileInfoBlock *fib, LONG *error_ptr);
STATIC BPTR Action_ParentFH(struct FileNode *fn, LONG *error_ptr);
STATIC BPTR Action_CopyDirFH(struct FileNode *fn, LONG *error_ptr);
STATIC LONG Action_FHFromLock(struct FileHandle *fh, struct FileLock *fl, LONG *error_ptr);
STATIC LONG Action_RenameDisk(APTR bcpl_name, LONG *error_ptr);
STATIC LONG Action_ChangeMode(LONG type, APTR object, LONG new_mode, LONG *error_ptr);
STATIC LONG Action_WriteProtect(LONG flag, ULONG key, LONG *error_ptr);
STATIC LONG Action_MoreCache(LONG buffer_delta, LONG *error_ptr);
STATIC LONG Action_SetComment(struct FileLock *parent, APTR bcpl_name, APTR bcpl_comment, LONG *error_ptr);
STATIC VOID HandleFileSystem(STRPTR device_name, STRPTR volume_name, STRPTR service_name);
/****************************************************************************/
VOID ReportError(STRPTR fmt,...);
VOID SPrintf(STRPTR buffer, STRPTR formatString,...);
/****************************************************************************/
struct Library * SysBase;
struct Library * DOSBase;
struct Library * UtilityBase;
struct Library * SocketBase;
struct Library * LocaleBase;
struct Locale * Locale;
struct Device * TimerBase;
struct timerequest TimerRequest;
struct Library * IconBase;
/****************************************************************************/
int errno;
int h_errno;
/****************************************************************************/
STATIC struct DosList * DeviceNode;
STATIC BOOL DeviceNodeAdded;
STATIC struct DosList * VolumeNode;
STATIC BOOL VolumeNodeAdded;
STATIC struct MsgPort * FileSystemPort;
STATIC smba_server_t * ServerData;
STATIC BOOL Quit;
STATIC BOOL Quiet;
STATIC BOOL CaseSensitive;
STATIC BOOL OmitHidden;
STATIC BOOL WriteProtected;
STATIC ULONG WriteProtectKey;
STATIC struct MinList FileList;
STATIC struct MinList LockList;
STATIC APTR MemoryPool;
STATIC struct RDArgs * Parameters;
STATIC struct DiskObject * Icon;
STATIC struct WBStartup * WBStartup;
STATIC struct MinList ErrorList;
STATIC STRPTR NewProgramName;
STATIC BOOL TranslateNames;
STATIC UBYTE A2M[256];
STATIC UBYTE M2A[256];
/****************************************************************************/
extern struct Library * FAR AbsExecBase;
/****************************************************************************/
LONG
Main(VOID)
{
struct
{
KEY Workgroup;
KEY UserName;
KEY Password;
SWITCH ChangeCase;
SWITCH CaseSensitive;
SWITCH OmitHidden;
SWITCH Quiet;
KEY ClientName;
KEY ServerName;
KEY DeviceName;
KEY VolumeName;
NUMBER CacheSize;
NUMBER DebugLevel;
KEY TranslationFile;
KEY Service;
} args;
STRPTR cmd_template =
"DOMAIN=WORKGROUP/K,"
"USER=USERNAME/K,"
"PASSWORD/K,"
"CHANGECASE/S,"
"CASE=CASESENSITIVE/S,"
"OMITHIDDEN/S,"
"QUIET/S,"
"CLIENT=CLIENTNAME/K,"
"SERVER=SERVERNAME/K,"
"DEVICE=DEVICENAME/K,"
"VOLUME=VOLUMENAME/K,"
"CACHE=CACHESIZE/N/K,"
"DEBUGLEVEL=DEBUG/N/K,"
"TRANSLATE=TRANSLATIONFILE/K,"
"SERVICE/A";
struct Process * this_process;
UBYTE program_name[MAX_FILENAME_LEN];
LONG result;
LONG number;
LONG cache_size = 0;
char env_workgroup_name[17];
char env_user_name[64];
char env_password[64];
geta4();
SysBase = AbsExecBase;
/* Pick up the Workbench startup message, if
* there is one.
*/
this_process = (struct Process *)FindTask(NULL);
if(this_process->pr_CLI == ZERO)
{
WaitPort(&this_process->pr_MsgPort);
WBStartup = (struct WBStartup *)GetMsg(&this_process->pr_MsgPort);
}
else
{
WBStartup = NULL;
}
/* Don't emit any debugging output before we are ready. */
SETDEBUGLEVEL(0);
/* Open the libraries we need and check
* whether we could get them.
*/
DOSBase = OpenLibrary("dos.library",0);
UtilityBase = OpenLibrary("utility.library",37);
if(UtilityBase == NULL || DOSBase == NULL || DOSBase->lib_Version < 37)
{
/* Complain loudly if this is not the operating
* system version we expected.
*/
if(DOSBase != NULL && this_process->pr_CLI != ZERO)
{
STRPTR msg = "AmigaOS 2.04 or higher required.\n";
Write(Output(),msg,strlen(msg));
}
Cleanup();
return(RETURN_FAIL);
}
/* This needs to be set up properly for error report
* to work.
*/
NewList((struct List *)&ErrorList);
memset(&args,0,sizeof(args));
/* If this program was launched from Workbench,
* parameter passing will have to be handled
* differently.
*/
if(WBStartup != NULL)
{
STRPTR str;
BPTR old_dir;
LONG n;
if(WBStartup->sm_NumArgs > 1)
n = 1;
else
n = 0;
/* Get the name of the program, as it was launched
* from Workbench. We actually prefer the name of
* the first project file, if there is one.
*/
strncpy(program_name,WBStartup->sm_ArgList[n].wa_Name,sizeof(program_name)-1);
program_name[sizeof(program_name)-1] = '\0';
/* Now open icon.library and read that icon. */
IconBase = OpenLibrary("icon.library",0);
if(IconBase == NULL)
{
ReportError("Could not open 'icon.library'.");
Cleanup();
return(RETURN_FAIL);
}
old_dir = CurrentDir(WBStartup->sm_ArgList[n].wa_Lock);
Icon = GetDiskObject(WBStartup->sm_ArgList[n].wa_Name);
CurrentDir(old_dir);
if(Icon == NULL)
{
ReportError("Icon not found.");
Cleanup();
return(RETURN_FAIL);
}
/* Examine the icon's tool types and use the
* information to fill the startup parameter
* data structure.
*/
str = FindToolType(Icon->do_ToolTypes,"DOMAIN");
if(str == NULL)
str = FindToolType(Icon->do_ToolTypes,"WORKGROUP");
if(str == NULL)
{
if(GetVar("smbfs_domain",env_workgroup_name,sizeof(env_workgroup_name),0) > 0 ||
GetVar("smbfs_workgroup",env_workgroup_name,sizeof(env_workgroup_name),0) > 0)
{
str = env_workgroup_name;
}
else
{
ReportError("Required 'WORKGROUP' parameter was not provided.");
Cleanup();
return(RETURN_ERROR);
}
}
args.Workgroup = str;
str = FindToolType(Icon->do_ToolTypes,"USER");
if(str == NULL)
str = FindToolType(Icon->do_ToolTypes,"USERNAME");
if(str == NULL)
{
if(GetVar("smbfs_user",env_user_name,sizeof(env_user_name),0) > 0 ||
GetVar("smbfs_username",env_user_name,sizeof(env_user_name),0) > 0)
{
str = env_user_name;
}
}
args.UserName = str;
str = FindToolType(Icon->do_ToolTypes,"PASSWORD");
if(str == NULL)
{
if(GetVar("smbfs_password",env_password,sizeof(env_password),0) > 0)
str = env_password;
}
args.Password = str;
if(FindToolType(Icon->do_ToolTypes,"CHANGECASE") != NULL)
args.ChangeCase = TRUE;
if(FindToolType(Icon->do_ToolTypes,"OMITHIDDEN") != NULL)
args.OmitHidden = TRUE;
if(FindToolType(Icon->do_ToolTypes,"QUIET") != NULL)
args.Quiet = TRUE;
if(FindToolType(Icon->do_ToolTypes,"CASE") != NULL ||
FindToolType(Icon->do_ToolTypes,"CASESENSITIVE") != NULL)
{
args.CaseSensitive = TRUE;
}
str = FindToolType(Icon->do_ToolTypes,"CLIENT");
if(str == NULL)
str = FindToolType(Icon->do_ToolTypes,"CLIENTNAME");
args.ClientName = str;
str = FindToolType(Icon->do_ToolTypes,"SERVER");
if(str == NULL)
str = FindToolType(Icon->do_ToolTypes,"SERVERNAME");
args.ServerName = str;
str = FindToolType(Icon->do_ToolTypes,"DEVICE");
if(str == NULL)
str = FindToolType(Icon->do_ToolTypes,"DEVICENAME");
args.DeviceName = str;
str = FindToolType(Icon->do_ToolTypes,"VOLUME");
if(str == NULL)
str = FindToolType(Icon->do_ToolTypes,"VOLUMENAME");
args.VolumeName = str;
str = FindToolType(Icon->do_ToolTypes,"TRANSLATE");
if(str == NULL)
str = FindToolType(Icon->do_ToolTypes,"TRANSLATIONFILE");
args.TranslationFile = str;
str = FindToolType(Icon->do_ToolTypes,"SERVICE");
args.Service = str;
if(str != NULL)
{
/* Set up the name of the program, as it will be
* displayed in error requesters.
*/
NewProgramName = AllocVec(strlen(WBStartup->sm_ArgList[0].wa_Name) + strlen(" ''") + strlen(str)+1,MEMF_ANY|MEMF_PUBLIC);
if(NewProgramName != NULL)
SPrintf(NewProgramName,"%s '%s'",WBStartup->sm_ArgList[0].wa_Name,str);
}
str = FindToolType(Icon->do_ToolTypes,"DEBUG");
if(str == NULL)
str = FindToolType(Icon->do_ToolTypes,"DEBUGLEVEL");
if(str != NULL)
{
if(StrToLong(str,&number) == -1)
{
ReportError("Invalid number '%s' for 'DEBUG' parameter.",str);
Cleanup();
return(RETURN_ERROR);
}
args.DebugLevel = &number;
}
str = FindToolType(Icon->do_ToolTypes,"CACHE");
if(str == NULL)
str = FindToolType(Icon->do_ToolTypes,"CACHESIZE");
if(str != NULL)
{
LONG number;
if(StrToLong(str,&number) == -1)
{
ReportError("Invalid number '%s' for 'CACHE' parameter.",str);
Cleanup();
return(RETURN_ERROR);
}
cache_size = number;
}
if(args.Workgroup == NULL)
{
ReportError("Required 'WORKGROUP' parameter was not provided.");
Cleanup();
return(RETURN_ERROR);
}
if(args.Service == NULL)
{
ReportError("'SERVICE' parameter needs an argument.");
Cleanup();
return(RETURN_ERROR);
}
}
else
{
GetProgramName(program_name,sizeof(program_name));
Parameters = ReadArgs(cmd_template,(LONG *)&args,NULL);
if(Parameters == NULL)
{
PrintFault(IoErr(),FilePart(program_name));
Cleanup();
return(RETURN_ERROR);
}
if(args.Workgroup == NULL)
{
if(GetVar("smbfs_domain",env_workgroup_name,sizeof(env_workgroup_name),0) > 0 ||
GetVar("smbfs_workgroup",env_workgroup_name,sizeof(env_workgroup_name),0) > 0)
{
args.Workgroup = env_workgroup_name;
}
else
{
ReportError("Required 'WORKGROUP' parameter was not provided.");
Cleanup();
return(RETURN_ERROR);
}
}
if(args.UserName == NULL)
{
if(GetVar("smbfs_user",env_user_name,sizeof(env_user_name),0) > 0 ||
GetVar("smbfs_username",env_user_name,sizeof(env_user_name),0) > 0)
{
args.UserName = env_user_name;
}
}
if(args.Password == NULL)
{
if(GetVar("smbfs_password",env_password,sizeof(env_password),0) > 0)
args.Password = env_password;
}
if(args.Service != NULL)
{
STRPTR name = FilePart(program_name);
/* Set up the name of the program, as it will be
* displayed in the proces status list.
*/
NewProgramName = AllocVec(strlen(name) + strlen(" ''") + strlen(args.Service)+1,MEMF_ANY|MEMF_PUBLIC);
if(NewProgramName != NULL)
SPrintf(NewProgramName,"%s '%s'",name,args.Service);
}
if(args.CacheSize != NULL)
cache_size = (*args.CacheSize);
}
/* Use the default if no user name is given. */
if(args.UserName == NULL)
args.UserName = "GUEST";
/* Use the default if no device or volume name is given. */
if(args.DeviceName == NULL && args.VolumeName == NULL)
args.DeviceName = "SMBFS";
CaseSensitive = (BOOL)args.CaseSensitive;
OmitHidden = (BOOL)args.OmitHidden;
/* Configure the debugging options. */
SETPROGRAMNAME(FilePart(program_name));
if(args.DebugLevel != NULL)
SETDEBUGLEVEL(*args.DebugLevel);
else
SETDEBUGLEVEL(0);
D(("%s (%s)",VERS,DATE));
if(Setup(
FilePart(program_name),
args.Service,
args.Workgroup,
args.UserName,
args.Password,
args.ChangeCase,
args.ClientName,
args.ServerName,
cache_size,
args.DeviceName,
args.VolumeName,
args.TranslationFile))
{
Quiet = args.Quiet;
if(Locale != NULL)
SHOWVALUE(Locale->loc_GMTOffset);
HandleFileSystem(args.DeviceName,args.VolumeName,args.Service);
result = RETURN_WARN;
}
else
{
result = RETURN_ERROR;
}
Cleanup();
return(result);
}
/****************************************************************************/
/* Obtain the descriptive text corresponding to an error number
* that may have been generated by the TCP/IP stack.
*/
STRPTR
amitcp_strerror(int error)
{
struct TagItem tags[2];
STRPTR result;
ENTER();
tags[0].ti_Tag = SBTM_GETVAL(SBTC_ERRNOSTRPTR);
tags[0].ti_Data = error;
tags[1].ti_Tag = TAG_END;
SocketBaseTagList(tags);
result = (STRPTR)tags[0].ti_Data;
RETURN(result);
return(result);
}
/****************************************************************************/
/* Return the descriptive text associated with a host lookup failure code. */
STRPTR
host_strerror(int error)
{
struct TagItem tags[2];
STRPTR result;
ENTER();
tags[0].ti_Tag = SBTM_GETVAL(SBTC_HERRNOSTRPTR);
tags[0].ti_Data = error;
tags[1].ti_Tag = TAG_END;
SocketBaseTagList(tags);
result = (STRPTR)tags[0].ti_Data;
RETURN(result);
return(result);
}
/****************************************************************************/
/* Compare two strings, either case sensitive or not
* sensitive to the case of the letters. How this is
* to be done is controlled by a global option. This
* routine is called whenever two SMB file names are
* to be compared.
*/
LONG
CompareNames(STRPTR a,STRPTR b)
{
LONG result;
if(CaseSensitive)
result = strcmp(a,b);
else
result = Stricmp(a,b);
return(result);
}
/****************************************************************************/
/* Translate a string into all upper case characters. */
VOID
StringToUpper(STRPTR s)
{
UBYTE c;
while((c = (*s)) != '\0')
(*s++) = ToUpper(c);
}
/****************************************************************************/
/* Prepare the accumulated list of error messages for display
* and purge the contents of that list.
*/
STATIC VOID
DisplayErrorList(VOID)
{
struct MinNode * last = NULL;
struct MinNode * mn;
STRPTR str = NULL;
STRPTR msg;
LONG len;
/* Determine how much memory will have to be
* allocated to hold all the accumulated
* error messages.
*/
len = 0;
for(mn = ErrorList.mlh_Head ;
mn->mln_Succ != NULL ;
mn = mn->mln_Succ)
{
last = mn;
msg = (STRPTR)(mn + 1);
len += strlen(msg)+1;
}
/* Allocate the memory for the messages, then
* copy them there.
*/
if(len > 0)
{
str = AllocVec(len,MEMF_ANY);
if(str != NULL)
{
str[0] = '\0';
for(mn = ErrorList.mlh_Head ;
mn->mln_Succ != NULL ;
mn = mn->mln_Succ)
{
msg = (STRPTR)(mn + 1);
strcat(str,msg);
if(mn != last)
strcat(str,"\n");
}
}
}
/* Purge the list. */
while((mn = (struct MinNode *)RemHead((struct List *)&ErrorList)) != NULL)
FreeVec(mn);
/* Display the error messages. */
if(str != NULL)
{
struct Library * IntuitionBase;
IntuitionBase = OpenLibrary("intuition.library",37);
if(IntuitionBase != NULL)
{
struct EasyStruct es;
STRPTR title;
memset(&es,0,sizeof(es));
if(NewProgramName == NULL)
title = WBStartup->sm_ArgList[0].wa_Name;
else
title = NewProgramName;
es.es_StructSize = sizeof(es);
es.es_Title = title;
es.es_TextFormat = str;
es.es_GadgetFormat = "Ok";
EasyRequestArgs(NULL,&es,NULL,NULL);
CloseLibrary(IntuitionBase);
}
FreeVec(str);
}
}
/* Add another error message to the list; the messages are
* collected so that they may be displayed together when
* necessary.
*/
STATIC VOID
AddError(STRPTR fmt,va_list args)
{
LONG len;
len = CVSPrintf(fmt,args);
if(len > 0)
{
struct MinNode * mn;
mn = AllocVec(sizeof(*mn) + len,MEMF_ANY|MEMF_PUBLIC);
if(mn != NULL)
{
STRPTR msg = (STRPTR)(mn + 1);
VSPrintf(msg,fmt,args);
AddTail((struct List *)&ErrorList,(struct Node *)mn);
}
}
}
/****************************************************************************/
/* Report an error that has occured; if the program was not launched
* from Shell, error messages will be accumulated for later display.
*/
VOID
ReportError(STRPTR fmt,...)
{
if(NOT Quiet)
{
va_list args;
if(WBStartup != NULL)
{
va_start(args,fmt);
AddError(fmt,args);
va_end(args);
}
else
{
UBYTE program_name[MAX_FILENAME_LEN];
GetProgramName(program_name,sizeof(program_name));
Printf("%s: ",FilePart(program_name));
va_start(args,fmt);
VPrintf(fmt,args);
va_end(args);
Printf("\n");
}
}
}
/****************************************************************************/
/* Release memory allocated from the global pool. */
VOID
FreeVecPooled(APTR address)
{
if(address != NULL)
{
ULONG * mem = address;
#if DEBUG
{
if(GETDEBUGLEVEL() > 0)
memset(address,0xA3,mem[-1] - sizeof(*mem));
}
#endif /* DEBUG */
AsmFreePooled(MemoryPool,&mem[-1],mem[-1],SysBase);
}
}
/* Allocate memory from the global pool. */
APTR
AllocVecPooled(ULONG size)
{
APTR result = NULL;
if(size > 0)
{
ULONG * mem;
size = (sizeof(*mem) + size + 7) & ~7;
mem = AsmAllocPooled(MemoryPool,size,SysBase);
if(mem != NULL)
{
(*mem++) = size;
#if DEBUG
{
if(GETDEBUGLEVEL() > 0)
memset(mem,0xA5,mem[-1] - sizeof(*mem));
}
#endif /* DEBUG */
result = mem;
}
}
return(result);
}
/****************************************************************************/
/* Obtain the number of seconds to add to the current time
* to translate local time into UTC.
*/
LONG
GetTimeZoneDelta(VOID)
{
LONG seconds;
if(Locale != NULL)
{
/* The GMT offset actually is the number of minutes to add to
* the local time to yield Greenwich Mean Time. It is negative
* for all time zones east of the Greenwich meridian and
* positive for all time zones west of it.
*/
seconds = 60 * Locale->loc_GMTOffset;
}
else
{
seconds = 0;
}
return(seconds);
}
/****************************************************************************/
/* Obtain the current time, in standard Unix format, adjusted for the
* local time zone.
*/
ULONG
GetCurrentTime(VOID)
{
struct timeval tv;
ULONG result;
GetSysTime((APTR)&tv);
result = UNIX_TIME_OFFSET + GetTimeZoneDelta() + tv.tv_secs;
return(result);
}
/****************************************************************************/
/* Fill in a 'tm' type time specification with time information
* corresponding to the number of seconds provided. Input is
* in Unix format.
*/
VOID
LocalTime(time_t seconds,struct tm * tm)
{
struct ClockData clock;
seconds -= GetTimeZoneDelta();
if(seconds < UNIX_TIME_OFFSET)
seconds = 0;
else
seconds -= UNIX_TIME_OFFSET;
Amiga2Date(seconds,&clock);
memset(tm,0,sizeof(*tm));
tm->tm_sec = clock.sec;
tm->tm_min = clock.min;
tm->tm_hour = clock.hour;
tm->tm_mday = clock.mday;
tm->tm_mon = clock.month - 1;
tm->tm_year = clock.year - 1900;
}
/* Calculate the number of seconds that have passed since January 1st 1970
* based upon the time specification provided. Output is in Unix format.
*/
time_t
MakeTime(const struct tm * const tm)
{
struct ClockData clock;
time_t seconds;
clock.sec = tm->tm_sec;
clock.min = tm->tm_min;
clock.hour = tm->tm_hour;
clock.mday = tm->tm_mday;
clock.month = tm->tm_mon + 1;
clock.year = tm->tm_year + 1900;
seconds = Date2Amiga(&clock) + UNIX_TIME_OFFSET + GetTimeZoneDelta();
return(seconds);
}
/****************************************************************************/
struct FormatContext
{
LONG fc_Size;
};
STATIC VOID ASM
CountChar(REG(a3) struct FormatContext * fc)
{
fc->fc_Size++;
}
/* Count the number of characters SPrintf() would put into a string. */
STATIC LONG
CVSPrintf(STRPTR format_string,va_list args)
{
struct FormatContext fc;
fc.fc_Size = 0;
RawDoFmt((STRPTR)format_string,args,(VOID (*)())CountChar,&fc);
return(fc.fc_Size);
}
/****************************************************************************/
STATIC VOID
VSPrintf(STRPTR buffer, STRPTR formatString, va_list args)
{
RawDoFmt(formatString,args,(VOID (*)())"\x16\xC0\x4E\x75",buffer);
}
/* Format a string for output. */
VOID
SPrintf(STRPTR buffer, STRPTR formatString,...)
{
va_list varArgs;
va_start(varArgs,formatString);
VSPrintf(buffer,formatString,varArgs);
va_end(varArgs);
}
/****************************************************************************/
/* NetBIOS broadcast name query code courtesy of Christopher R. Hertel.
* Thanks much, Chris!
*/
struct addr_entry
{
unsigned short flags;
unsigned char address[4];
};
struct nmb_header
{
unsigned short name_trn_id;
unsigned short flags;
unsigned short qdcount;
unsigned short ancount;
unsigned short nscount;
unsigned short arcount;
};
static UBYTE *
L1_Encode(UBYTE * dst, const UBYTE * name, const UBYTE pad, const UBYTE sfx)
{
int i = 0;
int j = 0;
int k;
while(('\0' != name[i]) && (i < 15))
{
k = ToUpper(name[i]);
i++;
dst[j++] = 'A' + ((k & 0xF0) >> 4);
dst[j++] = 'A' + (k & 0x0F);
}
i = 'A' + ((pad & 0xF0) >> 4);
k = 'A' + (pad & 0x0F);
while(j < 30)
{
dst[j++] = i;
dst[j++] = k;
}
dst[30] = 'A' + ((sfx & 0xF0) >> 4);
dst[31] = 'A' + (sfx & 0x0F);
dst[32] = '\0';
return(dst);
}
static int
L2_Encode(UBYTE * dst, const UBYTE * name, const UBYTE pad, const UBYTE sfx, const UBYTE * scope)
{
int lenpos;
int i;
int j;
if(NULL == L1_Encode(&dst[1], name, pad, sfx))
return(-1);
dst[0] = 0x20;
lenpos = 33;
if('\0' != (*scope))
{
do
{
for(i = 0, j = (lenpos + 1);
('.' != scope[i]) && ('\0' != scope[i]);
i++, j++)
{
dst[j] = ToUpper(scope[i]);
}
dst[lenpos] = (UBYTE)i;
lenpos += i + 1;
scope += i;
}
while('.' == (*scope++));
dst[lenpos] = '\0';
}
return(lenpos + 1);
}
int
BroadcastNameQuery(char *name, char *scope, UBYTE *address)
{
static const UBYTE header[12] =
{
0x07, 0xB0, /* 1964 == 0x07B0. */
0x01, 0x10, /* Binary 0 0000 0010001 0000 */
0x00, 0x01, /* One name query. */
0x00, 0x00, /* Zero answers. */
0x00, 0x00, /* Zero authorities. */
0x00, 0x00 /* Zero additional. */
};
static const UBYTE query_tail[4] =
{
0x00, 0x20,
0x00, 0x01
};
struct timeval tv;
fd_set read_fds;
int sock_fd;
int option_true = 1;
struct sockaddr_in sox;
struct nmb_header nmb_header;
UBYTE buffer[512];
int total_len;
int i,n;
int result;
ENTER();
sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sock_fd < 0)
{
SHOWMSG("couldn't get the socket");
result = (-errno);
goto out;
}
if(setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, &option_true, sizeof(option_true)) < 0)
{
SHOWMSG("couldn't enable the broadcast option");
result = (-errno);
goto out;
}
sox.sin_family = AF_INET;
sox.sin_port = htons(137);
sox.sin_addr.s_addr = htonl(0xFFFFFFFF);
memcpy(buffer, header, (total_len = sizeof(header)));
n = L2_Encode(&buffer[total_len], name, ' ', '\0', scope);
if(n < 0)
{
SHOWMSG("name encoding failed");
result = (-EINVAL);
goto out;
}
total_len += n;
memcpy(&buffer[total_len], query_tail, sizeof(query_tail));
total_len += sizeof(query_tail);
result = (-ENOENT);
n = 0;
/* Send the query packet; retry five times with a one second
* delay in between.
*/
for(i = 0 ; i < 5 ; i++)
{
if(sendto(sock_fd, (void *) buffer, total_len, 0, (struct sockaddr *)&sox, sizeof(struct sockaddr_in)) < 0)
{
SHOWMSG("could not send the packet");
result = (-errno);
goto out;
}
/* Wait for a response to arrive. */
tv.tv_secs = 1;
tv.tv_micro = 0;
FD_ZERO(&read_fds);
FD_SET(sock_fd,&read_fds);
if(WaitSelect(sock_fd+1, &read_fds, NULL, NULL, &tv, NULL) > 0)
{
n = recvfrom(sock_fd, buffer, sizeof(buffer), 0, NULL, NULL);
if(n < 0)
{
SHOWMSG("could not pick up the response packet");
result = (-errno);
goto out;
}
else if (n > 0)
{
break;
}
}
}
/* Did we get anything at all? */
if(n > sizeof(nmb_header))
{
/* Check whether the query was successful. */
memcpy(&nmb_header, buffer, sizeof(nmb_header));
if((nmb_header.flags & 0xF) == OK)
{
/* Find the NB/IP fields which directly follow
* the name.
*/
for(i = sizeof(header) + strlen(&buffer[sizeof(header)])+1 ; i < n - sizeof(query_tail) ; i++)
{
if(memcmp(&buffer[i], query_tail, sizeof(query_tail)) == SAME)
{
int start;
/* This should be the start of the interesting bits;
* we skip the NB/IP fields and the TTL field.
*/
start = i + sizeof(query_tail) + sizeof(long);
if(start < n)
{
unsigned short read_len;
struct addr_entry addr_entry;
/* This should be the read length. */
memcpy(&read_len, &buffer[start], 2);
/* Is there any useful and readable data attached? */
if(read_len >= sizeof(addr_entry) &&
start + sizeof(read_len) + sizeof(addr_entry) <= n)
{
/* Copy a single address entry; this should be
* just the one we need.
*/
memcpy(&addr_entry, &buffer[start + sizeof(read_len)], sizeof(addr_entry));
/* Copy the address field (IPv4 only). */
memcpy(address, addr_entry.address, 4);
result = 0;
}
}
break;
}
}
}
}
out:
if(sock_fd >= 0)
CloseSocket(sock_fd);
RETURN(result);
return(result);
}
/****************************************************************************/
/* Send a disk change notification message which will be picked up
* by all applications that listen for this kind of event, e.g.
* Workbench.
*/
STATIC VOID
SendDiskChange(ULONG class)
{
struct IOStdReq * input_request = NULL;
struct MsgPort * input_port;
struct InputEvent ie;
ENTER();
input_port = CreateMsgPort();
if(input_port == NULL)
goto out;
input_request = CreateIORequest(input_port,sizeof(*input_request));
if(input_request == NULL)
goto out;
if(OpenDevice("input.device",0,(struct IORequest *)input_request,0) != OK)
goto out;
memset(&ie,0,sizeof(ie));
ie.ie_Class = class;
ie.ie_Qualifier = IEQUALIFIER_MULTIBROADCAST;
GetSysTime(&ie.ie_TimeStamp);
input_request->io_Command = IND_WRITEEVENT;
input_request->io_Data = &ie;
input_request->io_Length = sizeof(ie);
DoIO((struct IORequest *)input_request);
out:
if(input_request != NULL)
{
if(input_request->io_Device != NULL)
CloseDevice((struct IORequest *)input_request);
DeleteIORequest((struct IORequest *)input_request);
}
DeleteMsgPort(input_port);
LEAVE();
}
/****************************************************************************/
/* Find the file node corresponding to a given name,
* skipping a particular entry if necessary.
*/
STATIC struct FileNode *
FindFileNode(STRPTR name,struct FileNode * skip)
{
struct FileNode * result = NULL;
struct FileNode * fn;
for(fn = (struct FileNode *)FileList.mlh_Head ;
fn->fn_MinNode.mln_Succ != NULL ;
fn = (struct FileNode *)fn->fn_MinNode.mln_Succ)
{
if(fn != skip && CompareNames(name,fn->fn_FullName) == SAME)
{
result = fn;
break;
}
}
return(result);
}
/* Find the lock node corresponding to a given name,
* skipping a particular entry if necessary.
*/
STATIC struct LockNode *
FindLockNode(STRPTR name,struct LockNode * skip)
{
struct LockNode * result = NULL;
struct LockNode * ln;
for(ln = (struct LockNode *)LockList.mlh_Head ;
ln->ln_MinNode.mln_Succ != NULL ;
ln = (struct LockNode *)ln->ln_MinNode.mln_Succ)
{
if(ln != skip && CompareNames(name,ln->ln_FullName) == SAME)
{
result = ln;
break;
}
}
return(result);
}
/* Check whether a new reference to be made to a named
* file will cause a conflict of access modes. No two
* files and locks may refer to the same object if
* either of these references is made in exclusive
* mode. This is the case which this function is
* trying to avoid.
*/
STATIC LONG
CheckAccessModeCollision(STRPTR name,LONG mode)
{
struct LockNode * ln;
struct FileNode * fn;
LONG error = OK;
ENTER();
SHOWSTRING(name);
fn = FindFileNode(name,NULL);
if(fn != NULL)
{
if(mode != SHARED_LOCK || fn->fn_Mode != SHARED_LOCK)
{
D(("collides with '%s'",fn->fn_FullName));
error = ERROR_OBJECT_IN_USE;
goto out;
}
}
ln = FindLockNode(name,NULL);
if(ln != NULL)
{
if(mode != SHARED_LOCK || ln->ln_FileLock.fl_Access != SHARED_LOCK)
{
D(("collides with '%s'",ln->ln_FullName));
error = ERROR_OBJECT_IN_USE;
goto out;
}
}
out:
RETURN(error);
return(error);
}
/* Find out whether there already exists a reference to a
* certain file or directory.
*/
STATIC LONG
NameAlreadyInUse(STRPTR name)
{
LONG error = OK;
ENTER();
if(FindFileNode(name,NULL) != NULL)
{
error = ERROR_OBJECT_IN_USE;
goto out;
}
if(FindLockNode(name,NULL) != NULL)
{
error = ERROR_OBJECT_IN_USE;
goto out;
}
out:
RETURN(error);
return(error);
}
/* Check whether an Amiga file name uses special characters which
* should be avoided when used with the SMB file sharing protocol.
*/
STATIC BOOL
IsReservedName(STRPTR name)
{
BOOL result = FALSE;
/* Disallow "." and "..". */
if(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
{
result = TRUE;
}
else
{
UBYTE c;
/* Disallow the use of the backslash in file names. */
while((c = (*name++)) != '\0')
{
if(c == SMB_PATH_SEPARATOR)
{
result = TRUE;
break;
}
}
}
return(result);
}
/****************************************************************************/
/* Convert a POSIX error code into an AmigaDOS error code. */
STATIC LONG
MapErrnoToIoErr(int error)
{
/* Not all of these mappings make good sense; bear in mind that
* POSIX covers more than a hundred different error codes
* whereas with AmigaDOS we're stranded with a measly 48...
*/
STATIC const LONG Map[][2] =
{
EPERM, ERROR_OBJECT_NOT_FOUND, /* Operation not permitted */
ENOENT, ERROR_OBJECT_NOT_FOUND, /* No such file or directory */
ESRCH, ERROR_OBJECT_NOT_FOUND, /* No such process */
EINTR, ERROR_BREAK, /* Interrupted system call */
EIO, ERROR_OBJECT_IN_USE, /* Input/output error */
E2BIG, ERROR_TOO_MANY_ARGS, /* Argument list too long */
EBADF, ERROR_INVALID_LOCK, /* Bad file descriptor */
ENOMEM, ERROR_NO_FREE_STORE, /* Cannot allocate memory */
EACCES, ERROR_OBJECT_IN_USE, /* Permission denied */
ENOTBLK, ERROR_OBJECT_WRONG_TYPE, /* Block device required */
EBUSY, ERROR_OBJECT_IN_USE, /* Device busy */
EEXIST, ERROR_OBJECT_EXISTS, /* File exists */
EXDEV, ERROR_NOT_IMPLEMENTED, /* Cross-device link */
ENOTDIR, ERROR_OBJECT_WRONG_TYPE, /* Not a directory */
EISDIR, ERROR_OBJECT_WRONG_TYPE, /* Is a directory */
EINVAL, ERROR_BAD_NUMBER, /* Invalid argument */
EFBIG, ERROR_DISK_FULL, /* File too large */
ENOSPC, ERROR_DISK_FULL, /* No space left on device */
ESPIPE, ERROR_SEEK_ERROR, /* Illegal seek */
EROFS, ERROR_WRITE_PROTECTED, /* Read-only file system */
EMLINK, ERROR_TOO_MANY_LEVELS, /* Too many links */
ENOTSOCK, ERROR_OBJECT_WRONG_TYPE, /* Socket operation on non-socket */
EDESTADDRREQ, ERROR_REQUIRED_ARG_MISSING, /* Destination address required */
EMSGSIZE, ERROR_LINE_TOO_LONG, /* Message too long */
EPROTOTYPE, ERROR_BAD_TEMPLATE, /* Protocol wrong type for socket */
ENOPROTOOPT, ERROR_NOT_IMPLEMENTED, /* Protocol not available */
EPROTONOSUPPORT, ERROR_NOT_IMPLEMENTED, /* Protocol not supported */
ESOCKTNOSUPPORT, ERROR_NOT_IMPLEMENTED, /* Socket type not supported */
EOPNOTSUPP, ERROR_NOT_IMPLEMENTED, /* Operation not supported */
EPFNOSUPPORT, ERROR_NOT_IMPLEMENTED, /* Protocol family not supported */
EAFNOSUPPORT, ERROR_NOT_IMPLEMENTED, /* Address family not supported by protocol family */
EADDRINUSE, ERROR_OBJECT_IN_USE, /* Address already in use */
EADDRNOTAVAIL, ERROR_OBJECT_NOT_FOUND, /* Can't assign requested address */
ENETDOWN, ERROR_OBJECT_NOT_FOUND, /* Network is down */
ENETUNREACH, ERROR_OBJECT_NOT_FOUND, /* Network is unreachable */
ENETRESET, ERROR_OBJECT_NOT_FOUND, /* Network dropped connection on reset */
ECONNABORTED, ERROR_OBJECT_NOT_FOUND, /* Software caused connection abort */
ECONNRESET, ERROR_OBJECT_NOT_FOUND, /* Connection reset by peer */
ENOBUFS, ERROR_DISK_FULL, /* No buffer space available */
EISCONN, ERROR_OBJECT_IN_USE, /* Socket is already connected */
ENOTCONN, ERROR_OBJECT_WRONG_TYPE, /* Socket is not connected */
ESHUTDOWN, ERROR_INVALID_LOCK, /* Can't send after socket shutdown */
ECONNREFUSED, ERROR_OBJECT_IN_USE, /* Connection refused */
ELOOP, ERROR_TOO_MANY_LEVELS, /* Too many levels of symbolic links */
ENAMETOOLONG, ERROR_LINE_TOO_LONG, /* File name too long */
EHOSTDOWN, ERROR_OBJECT_NOT_FOUND, /* Host is down */
EHOSTUNREACH, ERROR_OBJECT_NOT_FOUND, /* No route to host */
ENOTEMPTY, ERROR_DIRECTORY_NOT_EMPTY, /* Directory not empty */
EPROCLIM, ERROR_TASK_TABLE_FULL, /* Too many processes */
EUSERS, ERROR_TASK_TABLE_FULL, /* Too many users */
EDQUOT, ERROR_DISK_FULL, /* Disc quota exceeded */
-1
};
LONG result = ERROR_ACTION_NOT_KNOWN;
LONG i;
ENTER();
if(error < 0)
error = (-error);
for(i = 0 ; Map[i][0] != -1 ; i++)
{
if(Map[i][0] == error)
{
result = Map[i][1];
break;
}
}
RETURN(result);
return(result);
}
/****************************************************************************/
/* Translate a BCPL style file name (i.e. length is in the first byte)
* via a mapping table.
*/
STATIC VOID INLINE
TranslateBName(UBYTE * name,UBYTE * map)
{
if(TranslateNames)
{
LONG len;
UBYTE c;
len = (*name++);
while(len-- > 0)
{
c = (*name);
(*name++) = map[c];
}
}
}
/* Translate a NUL terminated file name via a mapping table. */
STATIC VOID INLINE
TranslateCName(UBYTE * name,UBYTE * map)
{
if(TranslateNames)
{
UBYTE c;
while((c = (*name)) != '\0')
(*name++) = map[c];
}
}
/****************************************************************************/
/* Remove a DosList entry using the proper protocols. Note that
* this function can fail!
*/
STATIC BOOL
ReallyRemoveDosEntry(struct DosList * entry)
{
struct Message * mn;
struct MsgPort * port;
struct DosList * dl;
BOOL result = FALSE;
LONG kind,i;
if(entry->dol_Type == DLT_DEVICE)
kind = LDF_DEVICES;
else
kind = LDF_VOLUMES;
port = entry->dol_Task;
for(i = 0 ; i < 100 ; i++)
{
dl = AttemptLockDosList(LDF_WRITE|kind);
if(((ULONG)dl) <= 1)
dl = NULL;
if(dl != NULL)
{
RemDosEntry(entry);
UnLockDosList(LDF_WRITE|kind);
result = TRUE;
break;
}
while((mn = GetMsg(port)) != NULL)
ReplyPkt((struct DosPacket *)mn->mn_Node.ln_Name,DOSFALSE,ERROR_ACTION_NOT_KNOWN);
Delay(TICKS_PER_SECOND / 10);
}
return(result);
}
/****************************************************************************/
/* Release all resources allocated by the Setup() routine. */
STATIC VOID
Cleanup(VOID)
{
ENTER();
/* If any errors have cropped up, display them now before
* call it quits.
*/
DisplayErrorList();
if(NewProgramName != NULL)
{
FreeVec(NewProgramName);
NewProgramName = NULL;
}
if(Parameters != NULL)
{
FreeArgs(Parameters);
Parameters = NULL;
}
if(Icon != NULL)
{
FreeDiskObject(Icon);
Icon = NULL;
}
if(ServerData != NULL)
{
smba_disconnect(ServerData);
ServerData = NULL;
}
if(DeviceNode != NULL)
{
if(DeviceNodeAdded)
{
if(ReallyRemoveDosEntry(DeviceNode))
FreeDosEntry(DeviceNode);
}
else
{
FreeDosEntry(DeviceNode);
}
DeviceNode = NULL;
}
if(VolumeNode != NULL)
{
if(VolumeNodeAdded)
{
if(ReallyRemoveDosEntry(VolumeNode))
FreeDosEntry(VolumeNode);
SendDiskChange(IECLASS_DISKREMOVED);
}
else
{
FreeDosEntry(VolumeNode);
}
VolumeNode = NULL;
}
if(FileSystemPort != NULL)
{
struct Message * mn;
/* Return all queued packets; there should be none, though. */
while((mn = GetMsg(FileSystemPort)) != NULL)
ReplyPkt((struct DosPacket *)mn->mn_Node.ln_Name,DOSFALSE,ERROR_ACTION_NOT_KNOWN);
DeleteMsgPort(FileSystemPort);
FileSystemPort = NULL;
}
if(TimerBase != NULL)
{
CloseDevice((struct IORequest *)&TimerRequest);
TimerBase = NULL;
}
if(SocketBase != NULL)
{
CloseLibrary(SocketBase);
SocketBase = NULL;
}
if(UtilityBase != NULL)
{
CloseLibrary(UtilityBase);
UtilityBase = NULL;
}
if(IconBase != NULL)
{
CloseLibrary(IconBase);
IconBase = NULL;
}
if(Locale != NULL)
{
CloseLocale(Locale);
Locale = NULL;
}
if(LocaleBase != NULL)
{
CloseLibrary(LocaleBase);
LocaleBase = NULL;
}
if(MemoryPool != NULL)
{
AsmDeletePool(MemoryPool,SysBase);
MemoryPool = NULL;
}
if(DOSBase != NULL)
{
CloseLibrary(DOSBase);
DOSBase = NULL;
}
if(WBStartup != NULL)
{
Forbid();
ReplyMsg((struct Message *)WBStartup);
}
LEAVE();
}
/* Allocate all the necessary resources to get going. */
STATIC BOOL
Setup(
STRPTR program_name,
STRPTR service,
STRPTR workgroup,
STRPTR username,
STRPTR opt_password,
BOOL opt_changecase,
STRPTR opt_clientname,
STRPTR opt_servername,
int opt_cachesize,
STRPTR device_name,
STRPTR volume_name,
STRPTR translation_file)
{
BOOL result = FALSE;
int error;
UBYTE name[MAX_FILENAME_LEN];
LONG i;
ENTER();
NewList((struct List *)&FileList);
NewList((struct List *)&LockList);
MemoryPool = AsmCreatePool(MEMF_ANY|MEMF_PUBLIC,4096,4096,SysBase);
if(MemoryPool == NULL)
{
ReportError("Could not create memory pool.");
goto out;
}
LocaleBase = OpenLibrary("locale.library",38);
if(LocaleBase != NULL)
Locale = OpenLocale(NULL);
memset(&TimerRequest,0,sizeof(TimerRequest));
if(OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)&TimerRequest,0) != OK)
{
ReportError("Could not open 'timer.device'.");
goto out;
}
TimerBase = TimerRequest.tr_node.io_Device;
SocketBase = OpenLibrary("bsdsocket.library",3);
if(SocketBase == NULL)
{
ReportError("Could not open 'bsdsocket.library' V3; TCP/IP stack not running?");
goto out;
}
error = SocketBaseTags(
SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), &errno,
SBTM_SETVAL(SBTC_HERRNOLONGPTR), &h_errno,
SBTM_SETVAL(SBTC_LOGTAGPTR), program_name,
SBTM_SETVAL(SBTC_BREAKMASK), SIGBREAKF_CTRL_C,
TAG_END);
if(error != OK)
{
ReportError("Could not initialize 'bsdsocket.library' (%ld, %s).",error,amitcp_strerror(error));
goto out;
}
if(opt_changecase)
{
for(i = 0 ; i < strlen(opt_password) ; i++)
opt_password[i] = ToUpper(opt_password[i]);
}
TranslateNames = FALSE;
/* Read the translation file, if possible. */
if(translation_file != NULL)
{
LONG error = OK;
STRPTR msg = NULL;
BPTR file;
file = Open(translation_file,MODE_OLDFILE);
if(file != ZERO)
{
if(Read(file,A2M,256) != 256 ||
Read(file,M2A,256) != 256)
{
msg = "Could not read translation file";
error = IoErr();
}
Close(file);
}
else
{
msg = "Could not open translation file";
error = IoErr();
}
if(msg == NULL)
{
TranslateNames = TRUE;
}
else
{
UBYTE description[100];
Fault(error,NULL,description,sizeof(description));
for(i = ((int)strlen(description)) - 1 ; i >= 0 ; i--)
{
if(description[i] == '\n')
description[i] = '\0';
}
ReportError("%s '%s' (%ld, %s).",msg,translation_file,error,description);
goto out;
}
}
error = smba_start(service,workgroup,username,opt_password,opt_clientname,opt_servername,opt_cachesize,&ServerData);
if(error < 0)
goto out;
FileSystemPort = CreateMsgPort();
if(FileSystemPort == NULL)
{
ReportError("Could not create filesystem port.");
goto out;
}
Forbid();
/* If a device name was provided, check whether it is
* well-formed.
*/
if(device_name != NULL)
{
struct DosList * dl;
BOOL device_exists;
for(i = 0 ; i < strlen(device_name) ; i++)
{
if(device_name[i] == '/')
{
Permit();
ReportError("Device name '%s' cannot be used with AmigaDOS.",device_name);
goto out;
}
}
if(device_name[0] == '\0')
{
Permit();
ReportError("Device name '%s' cannot be used with AmigaDOS.",device_name);
goto out;
}
strncpy(name,device_name,255);
name[255] = '\0';
/* Chop off any trailing colons. */
for(i = strlen(name)-1 ; i >= 0 ; i--)
{
if(name[i] == ':')
name[i] = '\0';
}
if(device_name[0] == '\0')
{
Permit();
ReportError("Device name '%s:' cannot be used with AmigaDOS.",device_name);
goto out;
}
dl = LockDosList(LDF_READ|LDF_DEVICES);
device_exists = (BOOL)(FindDosEntry(dl,device_name,LDF_DEVICES) != NULL);
UnLockDosList(LDF_DEVICES);
if(device_exists)
{
Permit();
ReportError("Device name '%s:' is already taken.",device_name);
goto out;
}
}
else
{
struct DosList * dl;
BOOL device_exists;
/* Find a unique device name. */
for(i = 0 ; i < 100 ; i++)
{
SPrintf(name,"SMBFS%ld",i);
dl = LockDosList(LDF_READ|LDF_DEVICES);
device_exists = (BOOL)(FindDosEntry(dl,name,LDF_DEVICES) != NULL);
UnLockDosList(LDF_DEVICES);
if(NOT device_exists)
{
device_name = name;
break;
}
}
}
if(device_name != NULL)
{
DeviceNode = MakeDosEntry(name,DLT_DEVICE);
if(DeviceNode == NULL)
{
Permit();
ReportError("Could not create device node.");
goto out;
}
DeviceNode->dol_Task = FileSystemPort;
AddDosEntry(DeviceNode);
DeviceNodeAdded = TRUE;
}
Permit();
/* Now take care of the volume name; make sure that it is
* well-formed.
*/
if(volume_name == NULL)
strncpy(name,device_name,255);
else
strncpy(name,volume_name,255);
name[255] = '\0';
for(i = 0 ; i < strlen(name) ; i++)
{
if(name[i] == '/')
{
ReportError("Volume name '%s' cannot be used with AmigaDOS.",name);
goto out;
}
}
if(name[0] == '\0')
{
ReportError("Volume name '%s' cannot be used with AmigaDOS.",name);
goto out;
}
/* Chop off any trailing colons. */
for(i = strlen(name)-1 ; i >= 0 ; i--)
{
if(name[i] == ':')
name[i] = '\0';
}
VolumeNode = MakeDosEntry(name,DLT_VOLUME);
if(VolumeNode == NULL)
{
ReportError("Could not create volume node.");
goto out;
}
VolumeNode->dol_Task = FileSystemPort;
DateStamp(&VolumeNode->dol_misc.dol_volume.dol_VolumeDate);
VolumeNode->dol_misc.dol_volume.dol_DiskType = ID_DOS_DISK;
if(volume_name != NULL)
{
AddDosEntry(VolumeNode);
VolumeNodeAdded = TRUE;
SendDiskChange(IECLASS_DISKINSERTED);
}
SetProgramName(NewProgramName);
result = TRUE;
out:
RETURN(result);
return(result);
}
/****************************************************************************/
/* Convert a BCPL string into a standard NUL terminated 'C' string. */
STATIC VOID INLINE
ConvertBString(LONG max_len,STRPTR cstring,APTR bstring)
{
STRPTR from = bstring;
LONG len = from[0];
if(len > max_len-1)
len = max_len-1;
if(len > 0)
strncpy(cstring,from+1,len);
cstring[len] = '\0';
}
/* Convert a NUL terminated 'C' string into a BCPL string. */
STATIC VOID INLINE
ConvertCString(LONG max_len,APTR bstring,STRPTR cstring)
{
LONG len = strlen(cstring);
STRPTR to = bstring;
if(len > max_len-1)
len = max_len-1;
(*to++) = len;
memcpy(to,cstring,len);
}
/****************************************************************************/
/* Build the fully qualified name of a file or directory in reference
* to the name of the parent directory. This takes care of all the
* special cases, such as the root directory. The result will be converted
* to be in a form suitable for use with the SMB file sharing service.
*/
STATIC LONG
BuildFullName(
STRPTR parent_name,
STRPTR name,
STRPTR * result_ptr)
{
LONG error = OK;
STRPTR buffer;
LONG len;
ENTER();
SHOWSTRING(parent_name);
SHOWSTRING(name);
(*result_ptr) = NULL;
/* parent_name == NULL means 'root directory', and
* so does parent_name == "\\".
*
* name == NULL means 'use parent name', and so
* does name == "".
*/
if(name != NULL && name[0] == '\0')
name = NULL;
/* Relative to root directory? */
if(name != NULL && name[0] == ':')
{
parent_name = NULL;
while(name[0] == ':')
name++;
}
if(parent_name != NULL && strcmp(parent_name,SMB_ROOT_DIR_NAME) == SAME)
parent_name = NULL;
/* Now, how much room is needed for the complete
* path to fit into a buffer?
*/
len = 2;
if(parent_name != NULL)
len += strlen(parent_name) + 1;
if(name != NULL)
len += strlen(name) + 1;
buffer = AllocVecPooled(len+3);
if(buffer == NULL)
{
error = ERROR_NO_FREE_STORE;
goto out;
}
/* The path starts with the name of parent. */
if(parent_name != NULL)
{
if(parent_name[0] == SMB_PATH_SEPARATOR)
{
while(parent_name[1] == SMB_PATH_SEPARATOR)
parent_name++;
strcpy(buffer,parent_name);
}
else
{
buffer[0] = SMB_PATH_SEPARATOR;
strcpy(&buffer[1],parent_name);
}
}
else
{
buffer[0] = '\0';
}
/* If there's a name to add, do just that. */
if(name != NULL)
{
LONG diff,i,j,start;
if(buffer[0] == '\0')
{
buffer[0] = SMB_PATH_SEPARATOR;
buffer[1] = '\0';
}
/* Append the name. */
if(parent_name != NULL)
{
for(i = 0 ; i < strlen(buffer) ; i++)
{
if(buffer[i] == SMB_PATH_SEPARATOR)
buffer[i] = '/';
}
AddPart(buffer,name,len);
}
else
{
strcat(buffer,name);
}
/* Parse the path specification, eliminating // combinations. */
len = strlen(buffer);
i = len;
while(len > 1)
{
if((buffer[len-1] == '/') && (buffer[len-2] != '/') && (buffer[len-2] != ':'))
buffer[--len] = '\0';
i--;
if((i == 0) || (buffer[i] == ':') || (buffer[i-1] == ':'))
break;
if((i > 1) && (buffer[i] == '/') && (buffer[i-1] == '/') && (buffer[i-2] != ':') && (buffer[i-2] != '/'))
{
start = i;
i -= 2;
while((i > 0) && (buffer[i] != ':') && (buffer[i] != '/'))
i--;
if((buffer[i] == ':') || (buffer[i] == '/'))
i++;
j = i;
diff = start-i+1;
len -= diff;
while(j < len)
{
buffer[j] = buffer[j+diff];
j++;
}
buffer[len] = '\0';
i = len;
}
}
/* Last sanity check; there shouldn't be any isolated
* backslashes and no double backslashes should remain
* either.
*/
len = strlen(buffer);
if(len > 0)
{
LONG start;
if(buffer[len-1] == '/')
{
error = ERROR_INVALID_COMPONENT_NAME;
goto out;
}
if(buffer[0] == '/' && buffer[1] == '/')
{
for(i = 0 ; i <= len ; i++)
buffer[i] = buffer[i+1];
len--;
}
for(i = 0 ; i < len-1 ; i++)
{
if(buffer[i] == '/' && buffer[i+1] == '/')
{
error = ERROR_INVALID_COMPONENT_NAME;
goto out;
}
}
start = -1;
for(i = 0 ; i < len ; i++)
{
if(buffer[i] == ':')
start = i+1;
}
if(start != -1)
{
for(i = 0 ; i <= len - start + 1 ; i++)
buffer[i] = buffer[start+i];
}
len = strlen(buffer);
if(buffer[0] != '/')
{
for(i = len+1 ; i > 0 ; i--)
buffer[i] = buffer[i-1];
buffer[0] = '/';
len++;
}
for(i = 0 ; i < len ; i++)
{
if(buffer[i] == '/')
buffer[i] = SMB_PATH_SEPARATOR;
}
}
}
/* If the buffer remains empty, it means that the
* reference should be made to the root directory.
*/
if(buffer[0] == '\0' || (buffer[0] == SMB_PATH_SEPARATOR && buffer[1] == SMB_PATH_SEPARATOR && buffer[2] == '\0'))
strcpy(buffer,SMB_ROOT_DIR_NAME);
(*result_ptr) = buffer;
SHOWSTRING(buffer);
out:
if(error != OK)
FreeVecPooled(buffer);
RETURN(error);
return(error);
}
/****************************************************************************/
STATIC BPTR
Action_Parent(
struct FileLock * parent,
LONG * error_ptr)
{
BPTR result = ZERO;
STRPTR full_name = NULL;
STRPTR parent_name;
BOOL cleanup = TRUE;
struct LockNode * ln = NULL;
LONG error;
LONG i;
ENTER();
SHOWVALUE(parent);
if(parent != NULL)
{
struct LockNode * ln = (struct LockNode *)parent->fl_Key;
parent_name = ln->ln_FullName;
if(strcmp(parent_name,SMB_ROOT_DIR_NAME) == SAME)
parent_name = NULL;
}
else
{
parent_name = NULL;
}
error = BuildFullName(parent_name,NULL,&full_name);
if(error != OK)
goto out;
/* Now for the interesting bit. Is this already the
* root directory we're dealing with?
*/
if(strcmp(full_name,SMB_ROOT_DIR_NAME) == SAME)
goto out;
ln = AllocVecPooled(sizeof(*ln));
if(ln == NULL)
{
error = ERROR_NO_FREE_STORE;
goto out;
}
memset(ln,0,sizeof(*ln));
ln->ln_FileLock.fl_Key = (LONG)ln;
ln->ln_FileLock.fl_Access = SHARED_LOCK;
ln->ln_FileLock.fl_Task = FileSystemPort;
ln->ln_FileLock.fl_Volume = MKBADDR(VolumeNode);
ln->ln_FullName = full_name;
for(i = strlen(full_name)-1 ; i >= 0 ; i--)
{
if(i == 0)
{
strcpy(full_name,SMB_ROOT_DIR_NAME);
break;
}
else if (full_name[i] == SMB_PATH_SEPARATOR)
{
full_name[i] = '\0';
break;
}
}
SHOWSTRING(full_name);
error = smba_open(ServerData,full_name,&ln->ln_File);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
AddTail((struct List *)&LockList,(struct Node *)ln);
result = MKBADDR(&ln->ln_FileLock);
cleanup = FALSE;
SHOWVALUE(&ln->ln_FileLock);
out:
if(cleanup)
{
FreeVecPooled(full_name);
FreeVecPooled(ln);
}
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_DeleteObject(
struct FileLock * parent,
APTR bcpl_name,
LONG * error_ptr)
{
LONG result = DOSFALSE;
STRPTR full_name = NULL;
smba_file_t * file = NULL;
STRPTR parent_name;
STRPTR full_parent_name = NULL;
UBYTE name[MAX_FILENAME_LEN];
struct LockNode * ln;
smba_stat_t stat;
LONG error;
int i;
ENTER();
if(WriteProtected)
{
error = ERROR_DISK_WRITE_PROTECTED;
goto out;
}
SHOWVALUE(parent);
if(parent != NULL)
{
ln = (struct LockNode *)parent->fl_Key;
parent_name = ln->ln_FullName;
if(strcmp(parent_name,SMB_ROOT_DIR_NAME) == SAME)
parent_name = NULL;
}
else
{
parent_name = NULL;
}
ConvertBString(sizeof(name),name,bcpl_name);
TranslateCName(name,A2M);
error = BuildFullName(parent_name,name,&full_name);
if(error != OK)
goto out;
/* We need to find this file's parent directory, so that
* in case the directory contents are currently being
* examined, that process is restarted.
*/
full_parent_name = AllocVecPooled(strlen(full_name)+3);
if(full_parent_name == NULL)
{
error = ERROR_NO_FREE_STORE;
goto out;
}
strcpy(full_parent_name,full_name);
for(i = strlen(full_parent_name)-1 ; i >= 0 ; i--)
{
if(i == 0)
{
strcpy(full_parent_name,SMB_ROOT_DIR_NAME);
break;
}
else if (full_parent_name[i] == SMB_PATH_SEPARATOR)
{
full_parent_name[i] = '\0';
break;
}
}
ln = FindLockNode(full_parent_name,NULL);
if(ln != NULL)
ln->ln_RestartExamine = TRUE;
FreeVecPooled(full_parent_name);
full_parent_name = NULL;
SHOWSTRING(full_name);
error = smba_open(ServerData,full_name,&file);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
error = smba_getattr(file,&stat);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
smba_close(file);
file = NULL;
if(stat.is_dir)
{
SHOWMSG("removing a directory");
error = smba_rmdir(ServerData,full_name);
if(error < 0)
{
SHOWVALUE(error);
/* This is a little bit difficult to justify since
* the error code may indicate a different cause,
* but in practice 'EACCES' seems to be returned
* if the directory to remove is not empty.
*/
if(error == (-EACCES))
error = ERROR_DIRECTORY_NOT_EMPTY;
else
error = MapErrnoToIoErr(error);
goto out;
}
}
else
{
SHOWMSG("removing a file");
error = smba_remove(ServerData,full_name);
if(error < 0)
{
SHOWVALUE(error);
error = MapErrnoToIoErr(error);
goto out;
}
}
SHOWMSG("done.");
result = DOSTRUE;
out:
FreeVecPooled(full_name);
FreeVecPooled(full_parent_name);
if(file != NULL)
smba_close(file);
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC BPTR
Action_CreateDir(
struct FileLock * parent,
APTR bcpl_name,
LONG * error_ptr)
{
BPTR result = ZERO;
STRPTR full_name = NULL;
struct LockNode * ln = NULL;
STRPTR parent_name;
STRPTR dir_name = NULL;
smba_file_t * dir = NULL;
STRPTR base_name;
UBYTE name[MAX_FILENAME_LEN];
LONG error;
LONG i;
ENTER();
if(WriteProtected)
{
error = ERROR_DISK_WRITE_PROTECTED;
goto out;
}
SHOWVALUE(parent);
if(parent != NULL)
{
struct LockNode * ln = (struct LockNode *)parent->fl_Key;
parent_name = ln->ln_FullName;
if(strcmp(parent_name,SMB_ROOT_DIR_NAME) == SAME)
parent_name = NULL;
}
else
{
parent_name = NULL;
}
ConvertBString(sizeof(name),name,bcpl_name);
TranslateCName(name,A2M);
error = BuildFullName(parent_name,name,&full_name);
if(error != OK)
goto out;
dir_name = AllocVecPooled(strlen(full_name)+3);
if(dir_name == NULL)
{
error = ERROR_NO_FREE_STORE;
goto out;
}
strcpy(dir_name,full_name);
base_name = NULL;
for(i = strlen(dir_name)-1 ; i >= 0 ; i--)
{
if(i == 0)
{
base_name = full_name;
strcpy(dir_name,SMB_ROOT_DIR_NAME);
break;
}
else if (dir_name[i] == SMB_PATH_SEPARATOR)
{
base_name = &full_name[i+1];
dir_name[i] = '\0';
break;
}
}
ln = AllocVecPooled(sizeof(*ln));
if(ln == NULL)
{
error = ERROR_NO_FREE_STORE;
goto out;
}
memset(ln,0,sizeof(*ln));
ln->ln_FileLock.fl_Key = (LONG)ln;
ln->ln_FileLock.fl_Access = EXCLUSIVE_LOCK;
ln->ln_FileLock.fl_Task = FileSystemPort;
ln->ln_FileLock.fl_Volume = MKBADDR(VolumeNode);
ln->ln_FullName = full_name;
error = smba_open(ServerData,dir_name,&dir);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
error = smba_mkdir(dir,base_name);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
smba_close(dir);
dir = NULL;
SHOWSTRING(full_name);
error = smba_open(ServerData,full_name,&ln->ln_File);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
AddTail((struct List *)&LockList,(struct Node *)ln);
result = MKBADDR(&ln->ln_FileLock);
SHOWVALUE(&ln->ln_FileLock);
out:
if(dir != NULL)
smba_close(dir);
FreeVecPooled(dir_name);
if(result == ZERO)
{
FreeVecPooled(full_name);
FreeVecPooled(ln);
}
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC BPTR
Action_LocateObject(
struct FileLock * parent,
APTR bcpl_name,
LONG mode,
LONG * error_ptr)
{
BPTR result = ZERO;
STRPTR full_name = NULL;
struct LockNode * ln = NULL;
STRPTR parent_name;
UBYTE name[MAX_FILENAME_LEN];
LONG error;
ENTER();
SHOWVALUE(parent);
if(parent != NULL)
{
struct LockNode * ln = (struct LockNode *)parent->fl_Key;
parent_name = ln->ln_FullName;
if(strcmp(parent_name,SMB_ROOT_DIR_NAME) == SAME)
parent_name = NULL;
}
else
{
parent_name = NULL;
}
ConvertBString(sizeof(name),name,bcpl_name);
TranslateCName(name,A2M);
if(IsReservedName(FilePart(name)))
{
error = ERROR_OBJECT_NOT_FOUND;
goto out;
}
error = BuildFullName(parent_name,name,&full_name);
if(error != OK)
goto out;
ln = AllocVecPooled(sizeof(*ln));
if(ln == NULL)
{
error = ERROR_NO_FREE_STORE;
goto out;
}
memset(ln,0,sizeof(*ln));
ln->ln_FileLock.fl_Key = (LONG)ln;
ln->ln_FileLock.fl_Access = (mode != EXCLUSIVE_LOCK) ? SHARED_LOCK : EXCLUSIVE_LOCK;
ln->ln_FileLock.fl_Task = FileSystemPort;
ln->ln_FileLock.fl_Volume = MKBADDR(VolumeNode);
ln->ln_FullName = full_name;
error = CheckAccessModeCollision(full_name,ln->ln_FileLock.fl_Access);
if(error != OK)
goto out;
SHOWSTRING(full_name);
error = smba_open(ServerData,full_name,&ln->ln_File);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
AddTail((struct List *)&LockList,(struct Node *)ln);
result = MKBADDR(&ln->ln_FileLock);
SHOWVALUE(&ln->ln_FileLock);
out:
if(result == ZERO)
{
FreeVecPooled(full_name);
FreeVecPooled(ln);
}
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC BPTR
Action_CopyDir(
struct FileLock * lock,
LONG * error_ptr)
{
BPTR result = ZERO;
STRPTR full_name = NULL;
struct LockNode * source;
struct LockNode * ln = NULL;
LONG error = OK;
ENTER();
SHOWVALUE(lock);
if(lock == NULL)
{
SHOWMSG("zero lock");
goto out;
}
if(lock->fl_Access != SHARED_LOCK)
{
SHOWMSG("cannot duplicate exclusive lock");
error = ERROR_OBJECT_IN_USE;
goto out;
}
ln = AllocVecPooled(sizeof(*ln));
if(ln == NULL)
{
error = ERROR_NO_FREE_STORE;
goto out;
}
memset(ln,0,sizeof(*ln));
source = (struct LockNode *)lock->fl_Key;
full_name = AllocVecPooled(strlen(source->ln_FullName)+3);
if(full_name == NULL)
{
error = ERROR_NO_FREE_STORE;
goto out;
}
strcpy(full_name,source->ln_FullName);
ln->ln_FileLock.fl_Key = (LONG)ln;
ln->ln_FileLock.fl_Access = source->ln_FileLock.fl_Access;
ln->ln_FileLock.fl_Task = FileSystemPort;
ln->ln_FileLock.fl_Volume = MKBADDR(VolumeNode);
ln->ln_FullName = full_name;
SHOWSTRING(full_name);
error = smba_open(ServerData,full_name,&ln->ln_File);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
AddTail((struct List *)&LockList,(struct Node *)ln);
result = MKBADDR(&ln->ln_FileLock);
SHOWVALUE(&ln->ln_FileLock);
out:
if(result == ZERO)
{
FreeVecPooled(full_name);
FreeVecPooled(ln);
}
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_FreeLock(
struct FileLock * lock,
LONG * error_ptr)
{
LONG result = DOSTRUE;
struct LockNode * ln;
LONG error = OK;
ENTER();
SHOWVALUE(lock);
if(lock == NULL)
goto out;
ln = (struct LockNode *)lock->fl_Key;
Remove((struct Node *)ln);
smba_close(ln->ln_File);
FreeVecPooled(ln->ln_FullName);
FreeVecPooled(ln);
out:
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_SameLock(
struct FileLock * lock1,
struct FileLock * lock2,
LONG * error_ptr)
{
LONG result = DOSFALSE;
STRPTR name1;
STRPTR name2;
LONG error = OK;
ENTER();
SHOWVALUE(lock1);
SHOWVALUE(lock2);
if(lock1 != NULL)
{
struct LockNode * ln = (struct LockNode *)lock1->fl_Key;
name1 = ln->ln_FullName;
}
else
{
name1 = SMB_ROOT_DIR_NAME;
}
if(lock2 != NULL)
{
struct LockNode * ln = (struct LockNode *)lock2->fl_Key;
name2 = ln->ln_FullName;
}
else
{
name2 = SMB_ROOT_DIR_NAME;
}
SHOWSTRING(name1);
SHOWSTRING(name2);
if(Stricmp(name1,name2) == SAME)
result = DOSTRUE;
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_SetProtect(
struct FileLock * parent,
APTR bcpl_name,
LONG mask,
LONG * error_ptr)
{
LONG result = DOSFALSE;
STRPTR full_name = NULL;
smba_file_t * file = NULL;
STRPTR parent_name;
UBYTE name[MAX_FILENAME_LEN];
smba_stat_t stat;
LONG error;
ENTER();
if(WriteProtected)
{
error = ERROR_DISK_WRITE_PROTECTED;
goto out;
}
SHOWVALUE(parent);
if(parent != NULL)
{
struct LockNode * ln = (struct LockNode *)parent->fl_Key;
parent_name = ln->ln_FullName;
if(strcmp(parent_name,SMB_ROOT_DIR_NAME) == SAME)
parent_name = NULL;
}
else
{
parent_name = NULL;
}
ConvertBString(sizeof(name),name,bcpl_name);
TranslateCName(name,A2M);
error = BuildFullName(parent_name,name,&full_name);
if(error != OK)
goto out;
SHOWSTRING(full_name);
error = smba_open(ServerData,full_name,&file);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
memset(&stat,0,sizeof(stat));
mask ^= FIBF_READ | FIBF_WRITE | FIBF_EXECUTE | FIBF_DELETE;
stat.atime = -1;
stat.ctime = -1;
stat.mtime = -1;
stat.size = -1;
if((mask & (FIBF_WRITE|FIBF_DELETE)) != (FIBF_WRITE|FIBF_DELETE))
{
SHOWMSG("write protection enabled");
stat.is_wp = TRUE;
}
else
{
SHOWMSG("write protection disabled");
}
error = smba_setattr(file,&stat);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
result = DOSTRUE;
out:
FreeVecPooled(full_name);
if(file != NULL)
smba_close(file);
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_RenameObject(
struct FileLock * source_lock,
APTR source_bcpl_name,
struct FileLock * destination_lock,
APTR destination_bcpl_name,
LONG * error_ptr)
{
struct LockNode * ln;
LONG result = DOSFALSE;
STRPTR full_source_name = NULL;
STRPTR full_destination_name = NULL;
UBYTE name[MAX_FILENAME_LEN];
STRPTR parent_name;
LONG error;
ENTER();
if(WriteProtected)
{
error = ERROR_DISK_WRITE_PROTECTED;
goto out;
}
SHOWVALUE(source_lock);
SHOWVALUE(destination_lock);
if(source_lock != NULL)
{
ln = (struct LockNode *)source_lock->fl_Key;
parent_name = ln->ln_FullName;
if(strcmp(parent_name,SMB_ROOT_DIR_NAME) == SAME)
parent_name = NULL;
}
else
{
parent_name = NULL;
}
ConvertBString(sizeof(name),name,source_bcpl_name);
TranslateCName(name,A2M);
error = BuildFullName(parent_name,name,&full_source_name);
if(error != OK)
goto out;
if(destination_lock != NULL)
{
ln = (struct LockNode *)destination_lock->fl_Key;
parent_name = ln->ln_FullName;
if(strcmp(parent_name,SMB_ROOT_DIR_NAME) == SAME)
parent_name = NULL;
}
else
{
parent_name = NULL;
}
ConvertBString(sizeof(name),name,destination_bcpl_name);
TranslateCName(name,A2M);
error = BuildFullName(parent_name,name,&full_destination_name);
if(error != OK)
goto out;
error = NameAlreadyInUse(full_source_name);
if(error != OK)
goto out;
error = NameAlreadyInUse(full_destination_name);
if(error != OK)
goto out;
SHOWSTRING(full_source_name);
SHOWSTRING(full_destination_name);
error = smba_rename(ServerData,full_source_name,full_destination_name);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
result = DOSTRUE;
out:
FreeVecPooled(full_source_name);
FreeVecPooled(full_destination_name);
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_DiskInfo(
struct InfoData * id,
LONG * error_ptr)
{
LONG result = DOSTRUE;
LONG block_size;
LONG num_blocks;
LONG num_blocks_free;
LONG error;
ENTER();
memset(id,0,sizeof(*id));
if(WriteProtected)
id->id_DiskState = ID_WRITE_PROTECTED;
else
id->id_DiskState = ID_VALIDATED;
error = smba_statfs(ServerData,&block_size,&num_blocks,&num_blocks_free);
if(error >= 0)
{
SHOWMSG("got the disk data");
SHOWVALUE(block_size);
SHOWVALUE(num_blocks);
SHOWVALUE(num_blocks_free);
if(block_size <= 0)
block_size = 512;
if(block_size < 512)
{
num_blocks /= (512 / block_size);
num_blocks_free /= (512 / block_size);
}
else if (block_size > 512)
{
num_blocks *= (block_size / 512);
num_blocks_free *= (block_size / 512);
}
id->id_NumBlocks = num_blocks;
id->id_NumBlocksUsed = num_blocks - num_blocks_free;
id->id_BytesPerBlock = 512;
id->id_DiskType = ID_DOS_DISK;
id->id_VolumeNode = MKBADDR(VolumeNode);
id->id_InUse = NOT (IsListEmpty((struct List *)&FileList) && IsListEmpty((struct List *)&LockList));
if(id->id_NumBlocks == 0)
id->id_NumBlocks = 1;
if(id->id_NumBlocksUsed == 0)
id->id_NumBlocksUsed = 1;
}
else
{
SHOWMSG("could not get any disk data");
id->id_NumBlocks = 1;
id->id_NumBlocksUsed = 1;
id->id_BytesPerBlock = 512;
id->id_DiskType = ID_NO_DISK_PRESENT;
error = MapErrnoToIoErr(error);
result = DOSFALSE;
}
SHOWVALUE(id->id_NumBlocks);
SHOWVALUE(id->id_NumBlocksUsed);
SHOWVALUE(id->id_BytesPerBlock);
SHOWVALUE(id->id_DiskType);
SHOWVALUE(id->id_VolumeNode);
SHOWVALUE(id->id_InUse);
(*error_ptr) = error;
RETURN(result);
return(result);
}
STATIC LONG
Action_Info(
struct FileLock * lock,
struct InfoData * id,
LONG * error_ptr)
{
LONG result;
ENTER();
SHOWVALUE(lock);
if(lock == NULL || lock->fl_Volume != MKBADDR(VolumeNode))
{
SHOWMSG("volume node does not match");
result = DOSFALSE;
(*error_ptr) = ERROR_NO_DISK;
}
else
{
result = Action_DiskInfo(id,error_ptr);
}
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_ExamineObject(
struct FileLock * lock,
struct FileInfoBlock * fib,
LONG * error_ptr)
{
LONG result = DOSFALSE;
LONG error = OK;
ENTER();
SHOWVALUE(lock);
memset(fib,0,sizeof(*fib));
if(lock == NULL)
{
STRPTR volume_name = BADDR(VolumeNode->dol_Name);
LONG len = volume_name[0];
SHOWMSG("ZERO root lock");
memcpy(fib->fib_FileName+1,volume_name+1,len);
fib->fib_FileName[0] = len;
fib->fib_DirEntryType = ST_ROOT;
fib->fib_EntryType = ST_ROOT;
fib->fib_NumBlocks = 1;
fib->fib_Date = VolumeNode->dol_misc.dol_volume.dol_VolumeDate;
fib->fib_DiskKey = -1;
fib->fib_Protection = FIBF_OTR_READ|FIBF_OTR_EXECUTE|FIBF_OTR_WRITE|FIBF_OTR_DELETE|
FIBF_GRP_READ|FIBF_GRP_EXECUTE|FIBF_GRP_WRITE|FIBF_GRP_DELETE;
}
else
{
struct LockNode * ln = (struct LockNode *)lock->fl_Key;
LONG seconds;
smba_stat_t stat;
error = smba_getattr(ln->ln_File,&stat);
if(error < 0)
{
SHOWMSG("information not available");
error = MapErrnoToIoErr(error);
goto out;
}
seconds = stat.mtime - UNIX_TIME_OFFSET - GetTimeZoneDelta();
if(seconds < 0)
seconds = 0;
fib->fib_Date.ds_Days = (seconds / (24 * 60 * 60));
fib->fib_Date.ds_Minute = (seconds % (24 * 60 * 60)) / 60;
fib->fib_Date.ds_Tick = (seconds % 60) * TICKS_PER_SECOND;
SHOWSTRING(ln->ln_FullName);
if(strcmp(ln->ln_FullName,SMB_ROOT_DIR_NAME) == SAME)
{
STRPTR volume_name = BADDR(VolumeNode->dol_Name);
LONG len = volume_name[0];
SHOWMSG("root lock");
memcpy(fib->fib_FileName+1,volume_name+1,len);
fib->fib_FileName[0] = len;
fib->fib_DirEntryType = ST_ROOT;
fib->fib_EntryType = ST_ROOT;
fib->fib_NumBlocks = 1;
fib->fib_Protection = FIBF_OTR_READ|FIBF_OTR_EXECUTE|FIBF_OTR_WRITE|FIBF_OTR_DELETE|
FIBF_GRP_READ|FIBF_GRP_EXECUTE|FIBF_GRP_WRITE|FIBF_GRP_DELETE;
}
else
{
STRPTR name;
LONG i;
name = ln->ln_FullName;
for(i = strlen(name)-1 ; i >= 0 ; i--)
{
if(name[i] == SMB_PATH_SEPARATOR)
{
name = &name[i+1];
break;
}
}
/* Just checking: will the name fit? */
if(strlen(name) >= sizeof(fib->fib_FileName))
{
error = ERROR_INVALID_COMPONENT_NAME;
goto out;
}
ConvertCString(sizeof(fib->fib_FileName),fib->fib_FileName,name);
TranslateBName(fib->fib_FileName,M2A);
fib->fib_DirEntryType = stat.is_dir ? ST_USERDIR : ST_FILE;
fib->fib_EntryType = fib->fib_DirEntryType;
fib->fib_NumBlocks = (stat.size + 511) / 512;
fib->fib_Size = stat.size;
fib->fib_Protection = FIBF_OTR_READ|FIBF_OTR_EXECUTE|FIBF_OTR_WRITE|FIBF_OTR_DELETE|
FIBF_GRP_READ|FIBF_GRP_EXECUTE|FIBF_GRP_WRITE|FIBF_GRP_DELETE;
if(stat.is_wp)
fib->fib_Protection ^= (FIBF_OTR_WRITE|FIBF_OTR_DELETE|FIBF_GRP_WRITE|FIBF_GRP_DELETE|FIBF_WRITE|FIBF_DELETE);
if(NOT stat.is_dir)
fib->fib_DiskKey = -1;
}
}
result = DOSTRUE;
D(("fib->fib_FileName = \"%b\"",MKBADDR(fib->fib_FileName)));
SHOWVALUE(fib->fib_DirEntryType);
SHOWVALUE(fib->fib_NumBlocks);
SHOWVALUE(fib->fib_Size);
SHOWVALUE(fib->fib_Date.ds_Days);
SHOWVALUE(fib->fib_Date.ds_Minute);
SHOWVALUE(fib->fib_Date.ds_Tick);
SHOWVALUE(fib->fib_DiskKey);
out:
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC BOOL
NameIsAcceptable(STRPTR name,LONG max_len)
{
BOOL result = FALSE;
UBYTE c;
/* This takes care of "." and "..". */
if(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
goto out;
/* Now for embedded '/', ':' and '\' characters and
* names that just don't want to fit.
*/
while((c = (*name++)) != '\0')
{
max_len--;
if(max_len == 0 || c == '/' || c == ':' || c == SMB_PATH_SEPARATOR)
goto out;
}
result = TRUE;
out:
return(result);
}
/****************************************************************************/
static int
dir_scan_callback_func_exnext(
struct FileInfoBlock * fib,
int fpos,
int nextpos,
char * name,
int eof,
smba_stat_t * stat)
{
int result;
ENTER();
D((" '%s'",name));
D((" is_dir=%ld is_wp=%ld is_hidden=%ld size=%ld",
stat->is_dir,stat->is_wp,stat->is_hidden,stat->size));
D((" fpos=%ld nextpos=%ld eof=%ld",fpos,nextpos,eof));
/* Skip file and drawer names that we wouldn't be
* able to handle in the first place.
*/
if(NameIsAcceptable((STRPTR)name,sizeof(fib->fib_FileName)) && NOT (stat->is_hidden && OmitHidden))
{
LONG seconds;
ConvertCString(sizeof(fib->fib_FileName),fib->fib_FileName,name);
TranslateBName(fib->fib_FileName,M2A);
fib->fib_DirEntryType = stat->is_dir ? ST_USERDIR : ST_FILE;
fib->fib_EntryType = fib->fib_DirEntryType;
fib->fib_NumBlocks = (stat->size + 511) / 512;
fib->fib_Size = stat->size;
fib->fib_Protection = FIBF_OTR_READ|FIBF_OTR_EXECUTE|FIBF_OTR_WRITE|FIBF_OTR_DELETE|
FIBF_GRP_READ|FIBF_GRP_EXECUTE|FIBF_GRP_WRITE|FIBF_GRP_DELETE;
if(stat->is_wp)
fib->fib_Protection ^= (FIBF_OTR_WRITE|FIBF_OTR_DELETE|FIBF_GRP_WRITE|FIBF_GRP_DELETE|FIBF_WRITE|FIBF_DELETE);
seconds = stat->mtime - UNIX_TIME_OFFSET - GetTimeZoneDelta();
if(seconds < 0)
seconds = 0;
fib->fib_Date.ds_Days = (seconds / (24 * 60 * 60));
fib->fib_Date.ds_Minute = (seconds % (24 * 60 * 60)) / 60;
fib->fib_Date.ds_Tick = (seconds % 60) * TICKS_PER_SECOND;
result = 1;
}
else
{
result = 0;
}
fib->fib_DiskKey = eof ? -1 : nextpos;
RETURN(result);
return(result);
}
STATIC LONG
Action_ExamineNext(
struct FileLock * lock,
struct FileInfoBlock * fib,
LONG * error_ptr)
{
struct LockNode * ln;
LONG result = DOSFALSE;
LONG error = OK;
long offset;
int count;
ENTER();
SHOWVALUE(lock);
if(fib->fib_DiskKey == -1)
{
SHOWMSG("scanning finished.");
error = ERROR_NO_MORE_ENTRIES;
goto out;
}
if(lock == NULL)
{
SHOWMSG("invalid lock");
error = ERROR_INVALID_LOCK;
goto out;
}
offset = fib->fib_DiskKey;
ln = (struct LockNode *)lock->fl_Key;
/* Check if we should restart scanning the directory
* contents. This is tricky at best and may produce
* irritating results :(
*/
if(ln->ln_RestartExamine)
{
offset = 0;
ln->ln_RestartExamine = FALSE;
}
memset(fib,0,sizeof(*fib));
SHOWMSG("calling 'smba_readdir'");
SHOWVALUE(offset);
count = smba_readdir(ln->ln_File,offset,fib,(smba_callback_t)dir_scan_callback_func_exnext);
SHOWVALUE(count);
if(count == 0 || fib->fib_FileName[0] == '\0')
{
SHOWMSG("nothing to be read");
fib->fib_DiskKey = -1;
error = ERROR_NO_MORE_ENTRIES;
goto out;
}
else if (count == (-EIO))
{
SHOWMSG("ouch! directory read error");
fib->fib_DiskKey = -1;
error = ERROR_NO_DEFAULT_DIR;
goto out;
}
else if (count < 0)
{
SHOWMSG("error whilst scanning");
SHOWVALUE(count);
fib->fib_DiskKey = -1;
error = MapErrnoToIoErr(count);
goto out;
}
result = DOSTRUE;
out:
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
struct ExAllContext
{
struct ExAllData * ec_Last;
struct ExAllData * ec_Next;
ULONG ec_BytesLeft;
ULONG ec_MinSize;
struct ExAllControl * ec_Control;
ULONG ec_Type;
LONG ec_Error;
BOOL ec_FirstAttempt;
};
static int
dir_scan_callback_func_exall(
struct ExAllContext * ec,
int fpos,
int nextpos,
char * name,
int eof,
smba_stat_t * stat)
{
int result = 0;
ENTER();
D((" '%s'",name));
D((" is_dir=%ld is_wp=%ld is_hidden=%ld size=%ld",
stat->is_dir,stat->is_wp,stat->is_hidden,stat->size));
D((" fpos=%ld nextpos=%ld eof=%ld",fpos,nextpos,eof));
/* Skip file and drawer names that we wouldn't be
* able to handle in the first place.
*/
if(NameIsAcceptable((STRPTR)name,MAX_FILENAME_LEN) && NOT (stat->is_hidden && OmitHidden))
{
struct ExAllData * ed;
ULONG size;
ULONG type = ec->ec_Type;
BOOL take_it;
size = (ec->ec_MinSize + strlen(name)+1 + 3) & ~3;
SHOWVALUE(size);
if(size > ec->ec_BytesLeft)
{
D(("size %ld > ec->ec_BytesLeft %ld",size,ec->ec_BytesLeft));
/* If this is the first directory entry,
* stop the entire process before it has
* really begun.
*/
if(ec->ec_FirstAttempt)
{
SHOWMSG("this was the first read attempt.");
ec->ec_Control->eac_Entries = 0;
ec->ec_Error = ERROR_NO_FREE_STORE;
}
else
{
SHOWMSG("try again");
ec->ec_Error = 0;
}
result = 1;
goto out;
}
ed = ec->ec_Next;
ed->ed_Next = NULL;
ed->ed_Name = (STRPTR)(((ULONG)ed) + ec->ec_MinSize);
strcpy(ed->ed_Name,name);
TranslateCName(ed->ed_Name,M2A);
if(type >= ED_TYPE)
ed->ed_Type = stat->is_dir ? ST_USERDIR : ST_FILE;
if(type >= ED_SIZE)
ed->ed_Size = stat->size;
if(type >= ED_PROTECTION)
{
ed->ed_Prot = FIBF_OTR_READ|FIBF_OTR_EXECUTE|FIBF_OTR_WRITE|FIBF_OTR_DELETE|
FIBF_GRP_READ|FIBF_GRP_EXECUTE|FIBF_GRP_WRITE|FIBF_GRP_DELETE;
if(stat->is_wp)
ed->ed_Prot ^= (FIBF_OTR_WRITE|FIBF_OTR_DELETE|FIBF_GRP_WRITE|FIBF_GRP_DELETE|FIBF_WRITE|FIBF_DELETE);
}
if(type >= ED_DATE)
{
LONG seconds;
seconds = stat->mtime - UNIX_TIME_OFFSET - GetTimeZoneDelta();
if(seconds < 0)
seconds = 0;
ed->ed_Days = (seconds / (24 * 60 * 60));
ed->ed_Mins = (seconds % (24 * 60 * 60)) / 60;
ed->ed_Ticks = (seconds % 60) * TICKS_PER_SECOND;
}
if(type >= ED_COMMENT)
ed->ed_Comment = "";
if(type >= ED_OWNER)
ed->ed_OwnerUID = ed->ed_OwnerGID = 0;
take_it = TRUE;
if(ec->ec_Control->eac_MatchString != NULL)
{
SHOWMSG("checking against match string");
if(NOT MatchPatternNoCase(ec->ec_Control->eac_MatchString,ed->ed_Name))
{
SHOWMSG("does not match");
take_it = FALSE;
}
}
if(take_it && ec->ec_Control->eac_MatchFunc != NULL)
{
SHOWMSG("calling match func");
/* NOTE: the order of the parameters passed to the match hook
* function can be somewhat confusing. For standard
* hook functions, the order of the parameters and the
* registers they go into is hook=A0, object=A2,
* message=A1. However, the documentation for the 'ExAll()'
* function always lists them in ascending order, that is
* hook=A0, message=A1, object=A2, which can lead to
* quite some confusion and strange errors.
*/
if(NOT CallHookPkt(ec->ec_Control->eac_MatchFunc,&type,ed))
{
SHOWMSG("does not match");
take_it = FALSE;
}
}
if(take_it)
{
SHOWMSG("registering new entry");
if(ec->ec_Last != NULL)
ec->ec_Last->ed_Next = ed;
ec->ec_Last = ed;
ec->ec_Next = (struct ExAllData *)(((ULONG)ed) + size);
ec->ec_BytesLeft -= size;
ec->ec_Control->eac_Entries++;
SHOWVALUE(ec->ec_Last->ed_Next);
SHOWVALUE(ed->ed_Name);
SHOWVALUE(ed->ed_Comment);
}
}
ec->ec_Control->eac_LastKey = (ULONG)(eof ? -1 : nextpos);
out:
ec->ec_FirstAttempt = FALSE;
RETURN(result);
return(result);
}
STATIC LONG
Action_ExamineAll(
struct FileLock * lock,
struct ExAllData * ed,
ULONG size,
ULONG type,
struct ExAllControl * eac,
LONG * error_ptr)
{
struct ExAllContext ec;
struct LockNode * ln;
LONG result = DOSFALSE;
LONG error = OK;
LONG offset;
int count;
ENTER();
SHOWVALUE(lock);
SHOWVALUE(eac->eac_LastKey);
eac->eac_Entries = 0;
ed->ed_Next = NULL;
if(eac->eac_LastKey == (ULONG)-1)
{
SHOWMSG("scanning finished.");
error = ERROR_NO_MORE_ENTRIES;
goto out;
}
if(lock == NULL)
{
SHOWMSG("invalid lock");
error = ERROR_INVALID_LOCK;
goto out;
}
if(type < ED_NAME || type > ED_OWNER)
{
D(("type %ld not supported",type));
error = ERROR_BAD_NUMBER;
goto out;
}
SHOWVALUE(type);
memset(&ec,0,sizeof(ec));
ec.ec_Next = ed;
ec.ec_BytesLeft = size;
ec.ec_Control = eac;
ec.ec_Type = type;
ec.ec_Error = ERROR_NO_MORE_ENTRIES;
ec.ec_FirstAttempt = TRUE;
switch(type)
{
case ED_NAME:
ec.ec_MinSize = offsetof(struct ExAllData,ed_Type);
break;
case ED_TYPE:
ec.ec_MinSize = offsetof(struct ExAllData,ed_Size);
break;
case ED_SIZE:
ec.ec_MinSize = offsetof(struct ExAllData,ed_Prot);
break;
case ED_PROTECTION:
ec.ec_MinSize = offsetof(struct ExAllData,ed_Days);
break;
case ED_DATE:
ec.ec_MinSize = offsetof(struct ExAllData,ed_Comment);
break;
case ED_COMMENT:
ec.ec_MinSize = offsetof(struct ExAllData,ed_OwnerUID);
break;
case ED_OWNER:
ec.ec_MinSize = sizeof(struct ExAllData);
break;
}
SHOWVALUE(ec.ec_MinSize);
offset = eac->eac_LastKey;
ln = (struct LockNode *)lock->fl_Key;
/* Check if we should restart scanning the directory
* contents. This is tricky at best and may produce
* irritating results :(
*/
if(ln->ln_RestartExamine)
{
offset = 0;
ln->ln_RestartExamine = FALSE;
}
if(offset == 0)
{
smba_stat_t stat;
SHOWMSG("first invocation");
SHOWMSG("getting file attributes");
error = smba_getattr(ln->ln_File,&stat);
if(error < 0)
{
SHOWMSG("didn't work");
error = MapErrnoToIoErr(error);
eac->eac_LastKey = (ULONG)-1;
goto out;
}
if(NOT stat.is_dir)
{
SHOWMSG("lock does not refer to a directory");
error = ERROR_OBJECT_WRONG_TYPE;
eac->eac_LastKey = (ULONG)-1;
goto out;
}
}
SHOWMSG("calling 'smba_readdir'");
SHOWVALUE(offset);
count = smba_readdir(ln->ln_File,offset,&ec,(smba_callback_t)dir_scan_callback_func_exall);
SHOWVALUE(count);
if(count == 0 || eac->eac_Entries == 0)
{
SHOWMSG("nothing to be read");
if(ec.ec_Error != OK)
{
SHOWMSG("flagging an error");
SHOWVALUE(ec.ec_Error);
eac->eac_LastKey = (ULONG)-1;
error = ec.ec_Error;
}
goto out;
}
else if (count == (-EIO))
{
SHOWMSG("ouch! directory read error");
eac->eac_LastKey = (ULONG)-1;
error = ERROR_NO_DEFAULT_DIR;
goto out;
}
else if (count < 0)
{
SHOWMSG("error whilst scanning");
eac->eac_LastKey = (ULONG)-1;
error = MapErrnoToIoErr(count);
goto out;
}
SHOWMSG("ok");
result = DOSTRUE;
out:
#if DEBUG
{
SHOWVALUE(eac->eac_Entries);
while(ed != NULL)
{
SHOWSTRING(ed->ed_Name);
ed = ed->ed_Next;
}
}
#endif /* DEBUG */
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_Find(
LONG action,
struct FileHandle * fh,
struct FileLock * parent,
APTR bcpl_name,
LONG * error_ptr)
{
LONG result = DOSFALSE;
STRPTR parent_path = NULL;
STRPTR full_name = NULL;
struct FileNode * fn = NULL;
STRPTR parent_name;
UBYTE name[MAX_FILENAME_LEN];
BOOL create_new_file;
LONG error;
ENTER();
switch(action)
{
case ACTION_FINDINPUT:
D(("ACTION_FINDINPUT [Open(\"%b\",MODE_OLDFILE)]",MKBADDR(bcpl_name)));
break;
case ACTION_FINDOUTPUT:
D(("ACTION_FINDOUTPUT [Open(\"%b\",MODE_NEWFILE)]",MKBADDR(bcpl_name)));
break;
case ACTION_FINDUPDATE:
D(("ACTION_FINDUPDATE [Open(\"%b\",MODE_READWRITE)]",MKBADDR(bcpl_name)));
break;
}
SHOWVALUE(parent);
if(parent != NULL)
{
struct LockNode * ln = (struct LockNode *)parent->fl_Key;
parent_name = ln->ln_FullName;
if(strcmp(parent_name,SMB_ROOT_DIR_NAME) == SAME)
parent_name = NULL;
}
else
{
parent_name = NULL;
}
ConvertBString(sizeof(name),name,bcpl_name);
TranslateCName(name,A2M);
if(IsReservedName(FilePart(name)))
{
error = ERROR_OBJECT_NOT_FOUND;
goto out;
}
error = BuildFullName(parent_name,name,&full_name);
if(error != OK)
goto out;
fn = AllocVecPooled(sizeof(*fn));
if(fn == NULL)
{
error = ERROR_NO_FREE_STORE;
goto out;
}
memset(fn,0,sizeof(*fn));
fn->fn_Handle = fh;
fn->fn_FullName = full_name;
fn->fn_Mode = (action == ACTION_FINDOUTPUT) ? EXCLUSIVE_LOCK : SHARED_LOCK;
error = CheckAccessModeCollision(full_name,fn->fn_Mode);
if(error != OK)
goto out;
SHOWSTRING(full_name);
if(action == ACTION_FINDOUTPUT)
{
/* Definitely create a new file. */
create_new_file = TRUE;
}
else if (action == ACTION_FINDINPUT)
{
/* Open an existing file for reading. */
create_new_file = FALSE;
}
else if (action == ACTION_FINDUPDATE)
{
smba_file_t * file = NULL;
smba_stat_t stat;
if(smba_open(ServerData,full_name,&file) == OK &&
smba_getattr(file,&stat) == OK)
{
/* File apparently opens Ok and information on it
* is available, don't try to replace it.
*/
create_new_file = FALSE;
}
else
{
/* We try to ignore the error here and assume
* that the remainder of the file opening
* procedure will produce a useful error
* report. In the mean time, assume that the
* file needs to be created.
*/
create_new_file = TRUE;
}
if(file != NULL)
smba_close(file);
}
else
{
/* What's that? */
error = ERROR_ACTION_NOT_KNOWN;
goto out;
}
/* Create a new file? */
if(create_new_file)
{
smba_stat_t stat;
smba_file_t * dir;
STRPTR base_name;
LONG i;
if(WriteProtected)
{
error = ERROR_DISK_WRITE_PROTECTED;
goto out;
}
parent_path = AllocVecPooled(strlen(full_name)+3);
if(parent_path == NULL)
{
error = ERROR_NO_FREE_STORE;
goto out;
}
strcpy(parent_path,full_name);
base_name = NULL;
for(i = strlen(parent_path)-1 ; i >= 0 ; i--)
{
if(i == 0)
{
base_name = full_name;
strcpy(parent_path,SMB_ROOT_DIR_NAME);
break;
}
else if (parent_path[i] == SMB_PATH_SEPARATOR)
{
base_name = &parent_path[i+1];
parent_path[i] = '\0';
break;
}
}
SHOWMSG("creating a file; finding parent path first");
SHOWSTRING(parent_path);
error = smba_open(ServerData,parent_path,&dir);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
/* Only one attribute counts: the file should not be write protected. */
memset(&stat,0,sizeof(stat));
SHOWMSG("now trying to create the file");
SHOWSTRING(base_name);
error = smba_create(dir,base_name,&stat);
if(error < 0)
{
SHOWMSG("didn't work.");
SHOWVALUE(error);
smba_close(dir);
error = MapErrnoToIoErr(error);
SHOWVALUE(error);
goto out;
}
SHOWMSG("good.");
smba_close(dir);
}
/* Now for the remainder... */
error = smba_open(ServerData,full_name,&fn->fn_File);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
fh->fh_Arg1 = (LONG)fn;
AddTail((struct List *)&FileList,(struct Node *)fn);
result = DOSTRUE;
out:
if(result == DOSFALSE)
{
FreeVecPooled(full_name);
FreeVecPooled(fn);
}
FreeVecPooled(parent_path);
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_Read(
struct FileNode * fn,
APTR mem,
LONG length,
LONG * error_ptr)
{
LONG result = 0;
LONG error = OK;
ENTER();
if(length > 0)
{
result = smba_read(fn->fn_File,mem,length,fn->fn_Offset);
if(result < 0)
{
error = MapErrnoToIoErr(result);
result = -1;
goto out;
}
fn->fn_Offset += result;
}
out:
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_Write(
struct FileNode * fn,
APTR mem,
LONG length,
LONG * error_ptr)
{
LONG result = DOSFALSE;
LONG error = OK;
ENTER();
if(WriteProtected)
{
error = ERROR_DISK_WRITE_PROTECTED;
goto out;
}
if(length > 0)
{
result = smba_write(fn->fn_File,mem,length,fn->fn_Offset);
if(result < 0)
{
error = MapErrnoToIoErr(result);
result = -1;
goto out;
}
fn->fn_Offset += result;
}
out:
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_End(
struct FileNode * fn,
LONG * error_ptr)
{
Remove((struct Node *)fn);
smba_close(fn->fn_File);
FreeVecPooled(fn->fn_FullName);
FreeVecPooled(fn);
(*error_ptr) = OK;
return(DOSTRUE);
}
/****************************************************************************/
STATIC LONG
Action_Seek(
struct FileNode * fn,
LONG position,
LONG mode,
LONG * error_ptr)
{
smba_stat_t stat;
LONG result;
LONG error;
LONG offset;
ENTER();
error = smba_getattr(fn->fn_File,&stat);
if(error < 0)
{
error = MapErrnoToIoErr(error);
result = -1;
goto out;
}
result = offset = fn->fn_Offset;
switch(mode)
{
case OFFSET_BEGINNING:
offset = position;
break;
case OFFSET_CURRENT:
offset += position;
break;
case OFFSET_END:
offset = stat.size + position;
break;
default:
error = ERROR_ACTION_NOT_KNOWN;
result = -1;
goto out;
}
if(offset < 0 || offset > stat.size)
{
error = ERROR_SEEK_ERROR;
result = -1;
goto out;
}
fn->fn_Offset = offset;
out:
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_SetFileSize(
struct FileNode * fn,
LONG position,
LONG mode,
LONG * error_ptr)
{
smba_stat_t stat;
LONG result = -1;
LONG error;
long offset;
ENTER();
if(WriteProtected)
{
error = ERROR_DISK_WRITE_PROTECTED;
goto out;
}
error = smba_getattr(fn->fn_File,&stat);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
offset = fn->fn_Offset;
switch(mode)
{
case OFFSET_BEGINNING:
offset = position;
break;
case OFFSET_CURRENT:
offset += position;
break;
case OFFSET_END:
offset = stat.size + position;
break;
default:
error = ERROR_ACTION_NOT_KNOWN;
goto out;
}
if(offset < 0)
{
error = ERROR_SEEK_ERROR;
goto out;
}
stat.atime = -1;
stat.ctime = -1;
stat.mtime = -1;
stat.size = offset;
error = smba_setattr(fn->fn_File,&stat);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
if(fn->fn_Offset > offset)
fn->fn_Offset = offset;
result = offset;
out:
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_SetDate(
struct FileLock * parent,
APTR bcpl_name,
struct DateStamp * ds,
LONG * error_ptr)
{
LONG result = DOSFALSE;
STRPTR full_name = NULL;
smba_file_t * file = NULL;
STRPTR parent_name;
UBYTE name[MAX_FILENAME_LEN];
smba_stat_t stat;
LONG seconds;
LONG error;
ENTER();
if(WriteProtected)
{
error = ERROR_DISK_WRITE_PROTECTED;
goto out;
}
SHOWVALUE(parent);
if(parent != NULL)
{
struct LockNode * ln = (struct LockNode *)parent->fl_Key;
parent_name = ln->ln_FullName;
if(strcmp(parent_name,SMB_ROOT_DIR_NAME) == SAME)
parent_name = NULL;
}
else
{
parent_name = NULL;
}
ConvertBString(sizeof(name),name,bcpl_name);
TranslateCName(name,A2M);
error = BuildFullName(parent_name,name,&full_name);
if(error != OK)
goto out;
SHOWSTRING(full_name);
error = smba_open(ServerData,full_name,&file);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
error = smba_getattr(file,&stat);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
seconds = (ds->ds_Days * 24 * 60 + ds->ds_Minute) * 60 + (ds->ds_Tick / TICKS_PER_SECOND);
stat.atime = -1;
stat.ctime = -1;
stat.mtime = UNIX_TIME_OFFSET + seconds + GetTimeZoneDelta();
stat.size = -1;
error = smba_setattr(file,&stat);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
result = DOSTRUE;
out:
FreeVecPooled(full_name);
if(file != NULL)
smba_close(file);
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_ExamineFH(
struct FileNode * fn,
struct FileInfoBlock * fib,
LONG * error_ptr)
{
LONG result = DOSFALSE;
smba_stat_t stat;
LONG error;
LONG seconds;
STRPTR name;
LONG i;
ENTER();
error = smba_getattr(fn->fn_File,&stat);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
name = fn->fn_FullName;
for(i = strlen(name)-1 ; i >= 0 ; i--)
{
if(name[i] == SMB_PATH_SEPARATOR)
{
name = &name[i+1];
break;
}
}
/* Just checking: will the name fit? */
if(strlen(name) >= sizeof(fib->fib_FileName))
{
error = ERROR_INVALID_COMPONENT_NAME;
goto out;
}
memset(fib,0,sizeof(*fib));
ConvertCString(sizeof(fib->fib_FileName),fib->fib_FileName,name);
TranslateBName(fib->fib_FileName,M2A);
fib->fib_DirEntryType = ST_FILE;
fib->fib_EntryType = ST_FILE;
fib->fib_NumBlocks = (stat.size + 511) / 512;
fib->fib_Size = stat.size;
fib->fib_DiskKey = -1;
seconds = stat.mtime - UNIX_TIME_OFFSET - GetTimeZoneDelta();
if(seconds < 0)
seconds = 0;
fib->fib_Date.ds_Days = (seconds / (24 * 60 * 60));
fib->fib_Date.ds_Minute = (seconds % (24 * 60 * 60)) / 60;
fib->fib_Date.ds_Tick = (seconds % 60) * TICKS_PER_SECOND;
result = DOSTRUE;
out:
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC BPTR
Action_ParentFH(
struct FileNode * fn,
LONG * error_ptr)
{
BPTR result = ZERO;
struct LockNode * ln = NULL;
LONG error;
STRPTR full_name;
LONG i;
ENTER();
full_name = AllocVecPooled(strlen(fn->fn_FullName)+3);
if(full_name == NULL)
{
error = ERROR_NO_FREE_STORE;
goto out;
}
strcpy(full_name,fn->fn_FullName);
for(i = strlen(full_name)-1 ; i >= 0 ; i--)
{
if(i == 0)
{
strcpy(full_name,SMB_ROOT_DIR_NAME);
break;
}
else if (full_name[i] == SMB_PATH_SEPARATOR)
{
full_name[i] = '\0';
break;
}
}
ln = AllocVecPooled(sizeof(*ln));
if(ln == NULL)
{
error = ERROR_NO_FREE_STORE;
goto out;
}
memset(ln,0,sizeof(*ln));
ln->ln_FileLock.fl_Key = (LONG)ln;
ln->ln_FileLock.fl_Access = SHARED_LOCK;
ln->ln_FileLock.fl_Task = FileSystemPort;
ln->ln_FileLock.fl_Volume = MKBADDR(VolumeNode);
ln->ln_FullName = full_name;
SHOWSTRING(full_name);
error = smba_open(ServerData,full_name,&ln->ln_File);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
AddTail((struct List *)&LockList,(struct Node *)ln);
result = MKBADDR(&ln->ln_FileLock);
SHOWVALUE(&ln->ln_FileLock);
out:
if(result == ZERO)
{
FreeVecPooled(ln);
FreeVecPooled(full_name);
}
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC BPTR
Action_CopyDirFH(
struct FileNode * fn,
LONG * error_ptr)
{
BPTR result = ZERO;
struct LockNode * ln = NULL;
STRPTR full_name = NULL;
LONG error;
ENTER();
if(fn->fn_Mode != SHARED_LOCK)
{
error = ERROR_OBJECT_IN_USE;
goto out;
}
full_name = AllocVecPooled(strlen(fn->fn_FullName)+3);
if(full_name == NULL)
{
error = ERROR_NO_FREE_STORE;
goto out;
}
strcpy(full_name,fn->fn_FullName);
ln = AllocVecPooled(sizeof(*ln));
if(ln == NULL)
{
error = ERROR_NO_FREE_STORE;
goto out;
}
memset(ln,0,sizeof(*ln));
ln->ln_FileLock.fl_Key = (LONG)ln;
ln->ln_FileLock.fl_Access = SHARED_LOCK;
ln->ln_FileLock.fl_Task = FileSystemPort;
ln->ln_FileLock.fl_Volume = MKBADDR(VolumeNode);
ln->ln_FullName = full_name;
SHOWSTRING(full_name);
error = smba_open(ServerData,full_name,&ln->ln_File);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
AddTail((struct List *)&LockList,(struct Node *)ln);
result = MKBADDR(&ln->ln_FileLock);
SHOWVALUE(&ln->ln_FileLock);
out:
if(result == ZERO)
{
FreeVecPooled(ln);
FreeVecPooled(full_name);
}
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_FHFromLock(
struct FileHandle * fh,
struct FileLock * fl,
LONG * error_ptr)
{
LONG result = DOSFALSE;
struct FileNode * fn;
struct LockNode * ln;
LONG error = OK;
ENTER();
SHOWVALUE(fl);
fn = AllocVecPooled(sizeof(*fn));
if(fn == NULL)
{
error = ERROR_NO_FREE_STORE;
goto out;
}
memset(fn,0,sizeof(*fn));
ln = (struct LockNode *)fl->fl_Key;
fn->fn_Handle = fh;
fn->fn_FullName = ln->ln_FullName;
fn->fn_File = ln->ln_File;
fn->fn_Mode = fl->fl_Access;
Remove((struct Node *)ln);
FreeVecPooled(ln);
fh->fh_Arg1 = (LONG)fn;
AddTail((struct List *)&FileList,(struct Node *)fn);
result = DOSTRUE;
out:
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_RenameDisk(
APTR bcpl_name,
LONG * error_ptr)
{
LONG result = DOSFALSE;
LONG error = OK;
STRPTR old_name;
STRPTR new_name;
UBYTE * name;
LONG len;
ENTER();
if(NOT VolumeNodeAdded)
{
error = ERROR_OBJECT_IN_USE;
goto out;
}
if(WriteProtected)
{
error = ERROR_DISK_WRITE_PROTECTED;
goto out;
}
/* Now for the really interesting part; the new name
* is to be a NUL-terminated BCPL string, and as such
* must be allocated via AllocVec().
*/
name = bcpl_name;
len = name[0];
new_name = AllocVec(1 + len + 1,MEMF_ANY|MEMF_PUBLIC);
if(new_name == NULL)
{
error = ERROR_NO_FREE_STORE;
goto out;
}
new_name[0] = len;
memcpy(&new_name[1],&name[1],len);
new_name[len+1] = '\0';
Forbid();
old_name = BADDR(VolumeNode->dol_Name);
FreeVec(old_name);
VolumeNode->dol_Name = MKBADDR(new_name);
Permit();
SendDiskChange(IECLASS_DISKINSERTED);
result = DOSTRUE;
out:
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_ChangeMode(
LONG type,
APTR object,
LONG new_mode,
LONG * error_ptr)
{
LONG result = DOSFALSE;
struct FileLock * fl = NULL;
struct FileNode * fn = NULL;
struct LockNode * ln = NULL;
STRPTR name;
LONG old_mode;
LONG error = OK;
ENTER();
/* Sanity check; verify parameters */
if((type != CHANGE_LOCK && type != CHANGE_FH) ||
(new_mode != EXCLUSIVE_LOCK && new_mode != SHARED_LOCK))
{
error = ERROR_ACTION_NOT_KNOWN;
goto out;
}
/* Now obtain the data structures, name and mode
* associated with the object in question.
*/
if(type == CHANGE_LOCK)
{
fl = object;
ln = (struct LockNode *)fl->fl_Key;
name = ln->ln_FullName;
old_mode = fl->fl_Access;
}
else
{
struct FileHandle * fh = object;
fn = (struct FileNode *)fh->fh_Arg1;
name = fn->fn_FullName;
old_mode = fn->fn_Mode;
}
/* Do we need to change anything at all? */
if(new_mode == old_mode)
{
result = DOSTRUE;
goto out;
}
/* This is the easiest case; change an
* exclusive access mode to a shared
* access mode. Since the original mode
* can be used by one object only,
* we get away by updating the mode
* value.
*/
if(new_mode == SHARED_LOCK)
{
if(type == CHANGE_LOCK)
fl->fl_Access = new_mode;
else
fn->fn_Mode = new_mode;
result = DOSTRUE;
goto out;
}
/* Is there another shared access lock
* which refers to the same object?
*/
if(FindLockNode(name,ln) != NULL)
{
error = ERROR_OBJECT_IN_USE;
goto out;
}
/* Is there another shared access file
* which refers to the same object?
*/
if(FindFileNode(name,fn) != NULL)
{
error = ERROR_OBJECT_IN_USE;
goto out;
}
/* There is just one single reference
* to this object; change the mode
* and quit.
*/
if(type == CHANGE_LOCK)
fl->fl_Access = new_mode;
else
fn->fn_Mode = new_mode;
result = DOSTRUE;
out:
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_WriteProtect(
LONG flag,
ULONG key,
LONG * error_ptr)
{
LONG result = DOSFALSE;
LONG error = OK;
ENTER();
if(flag == DOSFALSE)
{
if(WriteProtected)
{
if(key != WriteProtectKey)
{
error = ERROR_INVALID_LOCK;
goto out;
}
WriteProtected = FALSE;
if(VolumeNodeAdded)
{
SendDiskChange(IECLASS_DISKREMOVED);
SendDiskChange(IECLASS_DISKINSERTED);
}
}
}
else
{
if(NOT WriteProtected)
{
WriteProtected = TRUE;
WriteProtectKey = key;
if(VolumeNodeAdded)
{
SendDiskChange(IECLASS_DISKREMOVED);
SendDiskChange(IECLASS_DISKINSERTED);
}
}
else
{
error = ERROR_INVALID_LOCK;
goto out;
}
}
result = DOSTRUE;
out:
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_MoreCache(
LONG buffer_delta,
LONG * error_ptr)
{
LONG result;
int old_size;
ENTER();
old_size = smba_get_dircache_size(ServerData);
result = smba_change_dircache_size(ServerData,old_size + buffer_delta);
if(result == old_size && buffer_delta != 0)
{
result = DOSFALSE;
(*error_ptr) = ERROR_NO_FREE_STORE;
}
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC LONG
Action_SetComment(
struct FileLock * parent,
APTR bcpl_name,
APTR bcpl_comment,
LONG * error_ptr)
{
LONG result = DOSFALSE;
STRPTR full_name = NULL;
smba_file_t * file = NULL;
STRPTR parent_name;
UBYTE name[MAX_FILENAME_LEN];
UBYTE comment[80];
LONG error;
ENTER();
if(WriteProtected)
{
error = ERROR_DISK_WRITE_PROTECTED;
goto out;
}
SHOWVALUE(parent);
if(parent != NULL)
{
struct LockNode * ln = (struct LockNode *)parent->fl_Key;
parent_name = ln->ln_FullName;
if(strcmp(parent_name,SMB_ROOT_DIR_NAME) == SAME)
parent_name = NULL;
}
else
{
parent_name = NULL;
}
ConvertBString(sizeof(name),name,bcpl_name);
TranslateCName(name,A2M);
error = BuildFullName(parent_name,name,&full_name);
if(error != OK)
goto out;
SHOWSTRING(full_name);
error = smba_open(ServerData,full_name,&file);
if(error < 0)
{
error = MapErrnoToIoErr(error);
goto out;
}
ConvertBString(sizeof(comment),comment,bcpl_comment);
SHOWSTRING(comment);
/* All this work and we're only doing something very silly... */
if(strlen(comment) > 0)
{
error = ERROR_COMMENT_TOO_BIG;
goto out;
}
result = DOSTRUE;
out:
FreeVecPooled(full_name);
if(file != NULL)
smba_close(file);
(*error_ptr) = error;
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC VOID
HandleFileSystem(STRPTR device_name,STRPTR volume_name,STRPTR service_name)
{
BOOL sign_off = FALSE;
ULONG signals;
BOOL done;
ENTER();
DisplayErrorList();
if(NOT Quiet && WBStartup == NULL)
{
struct CommandLineInterface * cli;
cli = Cli();
if(NOT cli->cli_Background)
{
struct Process * this_process;
UBYTE name[MAX_FILENAME_LEN];
LONG max_cli;
LONG which;
LONG i;
this_process = (struct Process *)FindTask(NULL);
Forbid();
which = max_cli = MaxCli();
for(i = 1 ; i <= max_cli ; i++)
{
if(FindCliProc(i) == this_process)
{
which = i;
break;
}
}
Permit();
if(volume_name == NULL)
strncpy(name,device_name,sizeof(name)-1);
else
strncpy(name,volume_name,sizeof(name)-1);
name[sizeof(name)-1] = '\0';
for(i = strlen(name)-1 ; i >= 0 ; i--)
{
if(name[i] == ':')
name[i] = '\0';
else
break;
}
Printf("Connected '%s' to '%s:'; \"Break %ld\" or [Ctrl-C] to stop... ",
service_name,name,which);
Flush(Output());
sign_off = TRUE;
}
}
Quiet = TRUE;
done = FALSE;
do
{
signals = Wait(SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_F | (1UL << FileSystemPort->mp_SigBit));
if(signals & (1UL << FileSystemPort->mp_SigBit))
{
struct DosPacket * dp;
struct Message * mn;
LONG res1,res2;
while((mn = GetMsg(FileSystemPort)) != NULL)
{
dp = (struct DosPacket *)mn->mn_Node.ln_Name;
D(("got packet; sender '%s'",((struct Node *)dp->dp_Port->mp_SigTask)->ln_Name));
res2 = 0;
switch(dp->dp_Action)
{
case ACTION_DIE:
SHOWMSG("ACTION_DIE");
if(IsListEmpty((struct List *)&FileList) && IsListEmpty((struct List *)&LockList))
{
SHOWMSG("no locks or files pending; quitting");
res1 = DOSTRUE;
}
else
{
SHOWMSG("locks or files still pending; cannot quit yet");
res1 = DOSFALSE;
res2 = ERROR_OBJECT_IN_USE;
Quit = TRUE;
}
break;
case ACTION_CURRENT_VOLUME:
/* (Ignore) -> VolumeNode */
res1 = MKBADDR(VolumeNode);
break;
case ACTION_LOCATE_OBJECT:
/* Lock,Name,Mode -> Lock */
res1 = Action_LocateObject((struct FileLock *)BADDR(dp->dp_Arg1),(APTR)BADDR(dp->dp_Arg2),dp->dp_Arg3,&res2);
break;
case ACTION_RENAME_DISK:
/* Name -> Bool */
res1 = Action_RenameDisk((UBYTE *)BADDR(dp->dp_Arg1),&res2);
break;
case ACTION_FREE_LOCK:
/* Lock -> Bool */
res1 = Action_FreeLock((struct FileLock *)BADDR(dp->dp_Arg1),&res2);
break;
case ACTION_DELETE_OBJECT:
/* Lock,Name -> Bool */
res1 = Action_DeleteObject((struct FileLock *)BADDR(dp->dp_Arg1),(APTR)BADDR(dp->dp_Arg2),&res2);
break;
case ACTION_RENAME_OBJECT:
/* Source lock,source name,destination lock,destination name -> Bool */
res1 = Action_RenameObject((struct FileLock *)BADDR(dp->dp_Arg1),BADDR(dp->dp_Arg2),
(struct FileLock *)BADDR(dp->dp_Arg3),BADDR(dp->dp_Arg4),&res2);
break;
case ACTION_MORE_CACHE:
/* Buffer delta -> Total number of buffers */
/* NOTE: documentation for this packet type is inconsistent;
* in the 'good old' 1.x days 'res1' was documented as
* the total number of buffers to be returned. In the
* 2.x documentation it is said that 'res1' should
* return the success code, with 'res2' to hold the
* total number of buffers. However, the 'AddBuffers'
* shell command doesn't work that way, and the
* dos.library implementation of 'AddBuffers()' doesn't
* work that way either. The 1.3 'AddBuffers' command
* appears to treat a zero result as failure and a
* non-zero result as success, which suggests that this
* is how the packet is supposed to work, contrary to
* what the official documentation says.
*/
res1 = Action_MoreCache(dp->dp_Arg1,&res2);
break;
case ACTION_COPY_DIR:
/* Lock -> Lock */
res1 = Action_CopyDir((struct FileLock *)BADDR(dp->dp_Arg1),&res2);
break;
case ACTION_SET_PROTECT:
/* (Ignore),Lock,Name,Mask -> Bool */
res1 = Action_SetProtect((struct FileLock *)BADDR(dp->dp_Arg2),BADDR(dp->dp_Arg3),dp->dp_Arg4,&res2);
break;
case ACTION_CREATE_DIR:
/* Lock,Name -> Lock */
res1 = Action_CreateDir((struct FileLock *)BADDR(dp->dp_Arg1),(APTR)BADDR(dp->dp_Arg2),&res2);
break;
case ACTION_EXAMINE_OBJECT:
/* FileLock,FileInfoBlock -> Bool */
res1 = Action_ExamineObject((struct FileLock *)BADDR(dp->dp_Arg1),(struct FileInfoBlock *)BADDR(dp->dp_Arg2),&res2);
break;
case ACTION_EXAMINE_NEXT:
/* FileLock,FileInfoBlock -> Bool */
res1 = Action_ExamineNext((struct FileLock *)BADDR(dp->dp_Arg1),(struct FileInfoBlock *)BADDR(dp->dp_Arg2),&res2);
break;
case ACTION_DISK_INFO:
/* InfoData -> Bool */
Action_DiskInfo((struct InfoData *)BADDR(dp->dp_Arg1),&res2);
res1 = DOSTRUE;
res2 = 0;
break;
case ACTION_INFO:
/* FileLock,InfoData -> Bool */
res1 = Action_Info((struct FileLock *)BADDR(dp->dp_Arg1),(struct InfoData *)BADDR(dp->dp_Arg2),&res2);
break;
case ACTION_SET_COMMENT:
/* (Ignore),FileLock,Name,Comment -> Bool */
res1 = Action_SetComment((struct FileLock *)BADDR(dp->dp_Arg2),BADDR(dp->dp_Arg3),BADDR(dp->dp_Arg4),&res2);
break;
case ACTION_PARENT:
/* Lock -> Lock */
res1 = Action_Parent((struct FileLock *)BADDR(dp->dp_Arg1),&res2);
break;
case ACTION_INHIBIT:
SHOWMSG("ACTION_INHIBIT");
res1 = DOSTRUE;
break;
case ACTION_SET_DATE:
/* (Ignore),FileLock,Name,DateStamp(APTR) -> Bool */
res1 = Action_SetDate((struct FileLock *)BADDR(dp->dp_Arg2),(APTR)BADDR(dp->dp_Arg3),(struct DateStamp *)dp->dp_Arg4,&res2);
break;
case ACTION_SAME_LOCK:
/* Lock,Lock -> Bool */
res1 = Action_SameLock((struct FileLock *)BADDR(dp->dp_Arg1),(struct FileLock *)BADDR(dp->dp_Arg2),&res2);
break;
case ACTION_READ:
/* FileHandle->fh_Arg1,Buffer(APTR),Length -> Length */
res1 = Action_Read((struct FileNode *)dp->dp_Arg1,(APTR)dp->dp_Arg2,dp->dp_Arg3,&res2);
break;
case ACTION_WRITE:
/* FileHandle->fh_Arg1,Buffer(APTR),Length -> Length */
res1 = Action_Write((struct FileNode *)dp->dp_Arg1,(APTR)dp->dp_Arg2,dp->dp_Arg3,&res2);
break;
case ACTION_FINDUPDATE:
case ACTION_FINDINPUT:
case ACTION_FINDOUTPUT:
/* FileHandle,FileLock,Name -> Bool */
res1 = Action_Find(dp->dp_Action,(struct FileHandle *)BADDR(dp->dp_Arg1),(struct FileLock *)BADDR(dp->dp_Arg2),(APTR)BADDR(dp->dp_Arg3),&res2);
break;
case ACTION_END:
/* FileHandle->fh_Arg1 -> Bool */
res1 = Action_End((struct FileNode *)dp->dp_Arg1,&res2);
break;
case ACTION_SEEK:
/* FileHandle->fh_Arg1,Position,Mode -> Position */
res1 = Action_Seek((struct FileNode *)dp->dp_Arg1,dp->dp_Arg2,dp->dp_Arg3,&res2);
break;
case ACTION_SET_FILE_SIZE:
/* FileHandle->fh_Arg1,Offset,Mode -> New file size */
res1 = Action_SetFileSize((struct FileNode *)dp->dp_Arg1,dp->dp_Arg2,dp->dp_Arg3,&res2);
break;
case ACTION_WRITE_PROTECT:
/* Flag,Key -> Bool */
res1 = Action_WriteProtect(dp->dp_Arg1,dp->dp_Arg2,&res2);
break;
case ACTION_FH_FROM_LOCK:
/* FileHandle(BPTR),FileLock -> Bool */
res1 = Action_FHFromLock((struct FileHandle *)BADDR(dp->dp_Arg1),(struct FileLock *)BADDR(dp->dp_Arg2),&res2);
break;
case ACTION_IS_FILESYSTEM:
SHOWMSG("ACTION_IS_FILESYSTEM");
res1 = DOSTRUE;
break;
case ACTION_CHANGE_MODE:
/* Type,Object,Mode -> Bool */
res1 = Action_ChangeMode(dp->dp_Arg1,(APTR)BADDR(dp->dp_Arg2),dp->dp_Arg3,&res2);
break;
case ACTION_COPY_DIR_FH:
/* FileHandle->fh_Arg1 -> Bool */
res1 = Action_CopyDirFH((struct FileNode *)dp->dp_Arg1,&res2);
break;
case ACTION_PARENT_FH:
/* FileHandle->fh_Arg1 -> Bool */
res1 = Action_ParentFH((struct FileNode *)dp->dp_Arg1,&res2);
break;
case ACTION_EXAMINE_ALL:
/* FileLock,ExAllData(APTR),Size,Type,ExAllControl(APTR) -> Bool */
res1 = Action_ExamineAll((struct FileLock *)BADDR(dp->dp_Arg1),(struct ExAllData *)dp->dp_Arg2,
dp->dp_Arg3,dp->dp_Arg4,(struct ExAllControl *)dp->dp_Arg5,&res2);
break;
case ACTION_EXAMINE_FH:
/* FileHandle->fh_Arg1,FileInfoBlock -> Bool */
res1 = Action_ExamineFH((struct FileNode *)dp->dp_Arg1,(struct FileInfoBlock *)BADDR(dp->dp_Arg2),&res2);
break;
case ACTION_EXAMINE_ALL_END:
/* FileLock,ExAllData(APTR),Size,Type,ExAllControl(APTR) -> Bool */
res1 = DOSTRUE;
break;
default:
D(("Anything goes: dp->dp_Action=%ld (0x%lx)",dp->dp_Action,dp->dp_Action));
res1 = DOSFALSE;
res2 = ERROR_ACTION_NOT_KNOWN;
break;
}
SHOWVALUE(res1);
SHOWVALUE(res2);
ReplyPkt(dp,res1,res2);
D(("\n"));
}
}
#if DEBUG
{
if(signals & SIGBREAKF_CTRL_F)
{
struct FileNode * fn;
struct LockNode * ln;
D(("list of open files:"));
for(fn = (struct FileNode *)FileList.mlh_Head ;
fn->fn_MinNode.mln_Succ != NULL ;
fn = (struct FileNode *)fn->fn_MinNode.mln_Succ)
{
D((" name='%s'",fn->fn_FullName));
D((" mode=%ld, offset=%ld",fn->fn_Mode,fn->fn_Offset));
D((""));
}
D(("list of allocated locks:"));
for(ln = (struct LockNode *)LockList.mlh_Head ;
ln->ln_MinNode.mln_Succ != NULL ;
ln = (struct LockNode *)ln->ln_MinNode.mln_Succ)
{
D((" name='%s'",ln->ln_FullName));
D((" mode=%ld",ln->ln_FileLock.fl_Access));
D((""));
}
}
}
#endif /* DEBUG */
if(signals & SIGBREAKF_CTRL_C)
Quit = TRUE;
if(Quit)
{
if(IsListEmpty((struct List *)&FileList) && IsListEmpty((struct List *)&LockList))
{
SHOWMSG("no locks or files pending; quitting");
done = TRUE;
}
else
{
SHOWMSG("locks or files still pending; cannot quit yet");
}
}
}
while(NOT done);
if(sign_off)
Printf("stopped.\n");
LEAVE();
}